Запуск команд ос из с#-программы

№11-1,

технические науки

В статье рассматривается проблема запуска команд ОС из С#-программы.

Похожие материалы

Установка атрибутов файла

Получить информацию об атрибутах файла с помощью команд ОС можно, указав в свойстве Startlnfo.Arguments команду attrib и имя файла:

 using System; using System.Diagnostics;
class MainClass {
public static void Main() {
Process p = new Process(); 
p.StartInfo.FileName =  "cmd.exe"; 
p.StartInfo.Arguments = "/c attrib 104.exe"; 
p.StartInfo.UseShellExecute =  false; 
p.StartInfo.RedirectStandardOutput = true;
p.Start();
string output = p.StandardOutput.ReadToEnd(); Console.WriteLine("Output:");
Console.WriteLine(output); }
}

Полученная информация говорит о том, что файл 104.exe имеет атрибут А (архивный).

Если изменить свойство Startlnfo.Arguments на "/c attrib +S 104.exe", присвоим файлу ещё и атрибут S - 'системный':

При этом сам процесс присвоения никак не отражается в листинге: программа 105.exe в выходной поток ничего не направляет.

Получение структуры папки по команде tree

Из C#-программы можно получить структуру любой папки в виде дерева с помощью команды операционной системы tree. Воспользуемся для этого программой 106.cs.

//Программа 10 6.cs:   Структура папки в виде дерева.
//Имя папки указать  в командной строке при запуске программы.
using System;
using System.Diagnostics;
using System.IO;
class Class1 {
static void Main(string[]   args) {
string dir = args[0]; Console.WriteLine("Исследуется папка    "  +  dir +  "\n");
ProcessStartInfo  startinfo;
Process process = null;
string command,   stdoutline;
StreamReader stdoutreader;
//  Команда которую будет выполнять command = "tree ";
try
{
startinfo = new ProcessStartInfo();
//  Запускаем через cmd startinfo.FileName = "cmd.exe";
//  Ключ /c - выполнение команды
// Кроме команды можно вставить и необходимые параметры startinfo.Arguments = "/C " +  command +  dir;
//  Не используем shellexecute startinfo.UseShellExecute = false;
//  Перенаправить  вывод на обычную консоль
startinfo.RedirectStandardOutput = true;
//  Стартуем process = Process.Start(startinfo);
//  Получаем вывод stdoutreader = process.StandardOutput;
while((stdoutline=stdoutreader.ReadLine())!= null)
{
//  Выводим
Console.WriteLine(stdoutline);
}
stdoutreader.Close(); stdoutreader = null;
}
catch
{
throw;
}
finally
{
if   (process   != null)
{
//  Закрываем process.Close();
}
// Освобождаем process = null; startinfo = null;
}
Console.ReadLine();
}
}

Вывод на экран текстового файла командой type

Если в программе 106.cs вместо команды tree вставить команду type, получим вывод на экран текста указанного в командной строке файла ():

Создание виртуального диска

Используем программу 108.cs и команду subst

Надо только иметь в виду, что команда type выводит текст в коде ASCII. Для вывода текста в другой кодировке необходимо программу вывода перестроить операционной системы для создания виртуального диска Q:.

 //Программа 108.cs.   Создание виртуального диска
using System;
using System.Diagnostics;
using System.IO;
class Class1
{
static void Main(string[]   args) {
Console.WriteLine("Создаётся виртуальный диск  "  +  "\n");
ProcessStartInfo  startinfo; Process process = null;
string command, stdoutline;
StreamReader stdoutreader;
//  Команда которую будет выполнять  операционная система 
command = "subst q: ";
try {
startinfo = new ProcessStartInfo();
//  Запускаем через cmd startinfo.FileName = "cmd.exe";
//  Ключ /c - выполнение команды 
//Кроме команды можно вставить и необходимые параметры 
// Задаём папку, в которой будет создан виртуальный диск 
string dir = @"C:\Documents_and_Settings\All_Users"; 
startinfo.Arguments = "/C " +  command +  dir;
//  Не используем shellexecute startinfo.UseShellExecute = false; 
//  Перенаправить  вывод на обычную консоль
startinfo.RedirectStandardOutput = true;
//  Стартуем process = Process.Start(startinfo);
//  Получаем вывод stdoutreader = process.StandardOutput;
while((stdoutline=stdoutreader.ReadLine())!= null) {
//  Выводим
Console.WriteLine(stdoutline);
}
stdoutreader.Close();
stdoutreader = null;
}
catch
{
throw;
}
finally
{
if   (process   != null)
{
//  Закрываем process.Close();
}
// Освобождаем process = null; startinfo = null;
}
Console.ReadLine();
}
}

Проверка показывает, что в заданной папке создан виртуальный диск Q:.

Управление работой архиватора

Кроме программы WinRAR, имеющей оконный интерфейс, в комплект поставки архиватора входит файл Rar.exe. Это также 32-разрядная версия RAR для Windows, но она поддерживает только интерфейс командной строки и работает в текстовом (консольном) режиме. Консольную версию RAR удобно использовать для вызова из пакетных файлов (BAT и CMD), для запуска из приглашения DOS и т.п. Она поддерживает больше команд и ключей в командной строке, чем WinRAR.

Общий синтаксис командной строки для архивации файлов таков:

 Rar.exe A [-ключи ] <Архив> [ Файлы ] [ @Файлы-списки ]  

Например, если необходимо добавить файл copycat1.cs в архив LETTER.RAR, используется команда:

 rar.exe a letter.rar copycatl.cs 

Для сохранения в архиве файла используем программу 109.cs:

 //109.cs:  Управление архиватором из C#-программы
using System;
using System.Diagnostics;
using System.IO;
class Class1
{
static void Main(string[]   args) {
ProcessStartInfo  startinfo;
Process process = null;
string command;
//  Команда которую будет выполнять command = "  a ";
try {
string arhiv = "letter.rar"; string file = "copycat1.cs";
startinfo = new ProcessStartInfo();
//  Запускаем архиватор startinfo.FileName = "rar.exe"; 
//  Ключ /c - выполнение команды 
// Кроме команды можно вставить и необходимые параметры 
startinfo.Arguments = "/C "  +  command +  "  "  +  arhiv +  "  "  + file;
//  Не используем shellexecute startinfo.UseShellExecute = false;
//  Стартуем process = Process.Start(startinfo); }
catch {
throw;
}
finally {
if   (process != null) {
//  Закрываем process.Close();
}
// Освобождаем process = null; startinfo = null;
}
Console.ReadLine();
}
}

При старте программы 109.exe запускается программа rar.exe и сообщает через консоль, что создаётся архив letter.rar и в него добавляется файл copycat1.cs. Операция выполнена успешно, о чём свидетельствуют OK Done.

Создание командного файла из С#-программы.

Создание текстового файла (bat или cmd)

Для того чтобы создать командный файл необходимо создать поток для записи в файл.

StreamWriter s = new StreamWriter("1.cmd", false);

где "1.cmd" - это имя создаваемого файла;

false - определяет, требуется ли добавить в файл данные.Если файл существует и значение этого параметра равно false, файл перезаписывается; если не существует, создается новый файл.

После создания потока необходимо записать в файл соответствующие команды. Запись в файл производится командами класса System.Console, такими, как Write() или WriteLine(). Например, создать директорий с именем "1" можно по команде операционной системы md 1. Перед командой надо указать имя созданного потока:

s.WriteLine("md 1");

Вывод на экран созданного файла

Выведем на экран содержимое нашего командного файла. Для этого создаем поток для чтения из файла и построчно выводим на экран его содержимое.

StreamReader r = new StreamReader("1.cmd");
while (!r.EndOfStream)
{
string str = r.ReadLine(); Console.WriteLine(str);
}
r.Close();

Здесь EndOfStream свойство класса StreamReader, определяющее, находится ли позиция текущего потока в конце потока. Пример.

В папке С:\Моно\222> размещаем файл 110.cs, в котором предусмотрено создание командного файла операционной системы, содержащего всего одну команду: "md 1", а затем - чтение этого файла с выводом его содержимого на экран.

//Файл 110.cs: 
using System; 
using System.IO;
public class Show
{
public static void Main() {
StreamWriter s = new StreamWriter("1.cmd",
false);
s.WriteLine("md 1"); s.Close();
StreamReader r = new StreamReader("1.cmd");
while (!r.EndOfStream)
{
string str = r.ReadLine(); Console.WriteLine(str);
}
r.Close();
Console.Read(); }
}

После компиляции получаем файл 110.exe, при исполнении которого на экран выводится созданная команда:

В папке с программой 110.cs появился файл 1.cmd:

Проблема длинных имён и возможные пути её решения

В операционной системе Windows разрешено использование «длинных имён» файлов и папок (каталогов или директориев). По данным MSDN полная длина имени файла не должна превышать 260 символов, а длина пути до файла или папки не должна быть выше 248 символов.

Однако во время работы каталоги формируются не сразу, а постепенно. Из них выстраивается иерархическая структура, в которой общая длина имени папок и файлов может значительно превысить установленные пределы, так как длины имён файлов и папок стандартными средствами операционной системы не контролируются.

Если длина адреса (пути к файлу) превышает допустимые пределы, представим полный путь к файлу (Р) состоящим из двух частей: р1 (допустимая длина пути) и р2 (запредельный путь).

Операционная система файл, находящийся по адресу Р из корня диска не видит. Поэтому ни одна команда операционной системы с этим файлом работать не сможет, если его попытаться достать из активного каталога, находящегося близко к корню диска.

Добраться до этого файла можно, если переместить активный каталог ОС на границу допустимого и запредельного пути. В этом случае путь к файлу нужно задавать относительно активного каталога, так как полный адрес к файлу распадётся на две части -р1 и р2:

 C:\p1>copy p2 

Допустимый путь р1 указывает операционной системе, откуда надо начинать выполнение команды, а тот адрес, который раньше был запредельным, становится допустимым.

Эта особенность работы с длинными именами стандартными средствами операционных систем Windows не учитывается. Поэтому часто можно встретиться с отказом работы с некоторыми папками Windows программами - архиваторами, программами для переноса информации на компакт-диски и DVD-диски, и др.

При копировании или перемещении такой папки надо либо использовать нестандартную программу копирования (перемещения), которая будет делить полный путь на два: допустимый - и запредельный, и переносить активный каталог на границу этих двух миров, либо надо корректировать структуру такого неправильного каталога.

Коррекция структуры выгоднее, так как позволит с исправленным каталогом работать стандартными средствами.

Коррекцию можно произвести по-разному. Рассмотрим два возможных метода: метод 1 - «сжатие имён»; метод 2 -архивирование «запредельной части» (и таким образом - сжатие полного имени).

Для первого метода можно применить один из известных алгоритмов сжатия (например - Лемпеля-Зива, DES, и др.). Но для работы полученные новые имена нужно как-то связывать со старыми...

Для второго метода структура информации практически не изменяется. Нужно только применить какой- нибудь архиватор.

При реализации 2 метода задача будет заключаться в следующем:

  1. Начать копирование (перемещение) заданной папки стандартным образом.
  2. При возникновении исключения из-за длины пути, выделить из полного пути допустимый путь и в конец его перенести активный каталог (в операционной системе это делается с помощью команды cd).
  3. Переместить каталог, вызвавший исключение, в корень диска. Операционная система это позволит сделать, так как запредельный ранее каталог теперь стал доступным.
  4. Архиватором сжать перемещённый каталог.
  5. Полученный архив переместить на место, которое занимал запредельный каталог.
  6. Продолжить операцию копирования (перемещения).

Результатом этого будет откорректированный каталог, в котором запредельно длинных имён не будет. Такой каталог можно скопировать или переместить стандартными средствами ОС, а если нужно сохранить прежнюю структуру каталога, архив можно сделать саморазворачивающимся (SFX) и развернуть его после копирования на новое место.

Для ускорения работы архиватор можно использовать стандартный, например, RAR.

В данном случае решение задачи достигается за счёт комплексирования программных средств: С#-программы копирования (перемещения) каталогов, операторов ОС и посторонней программы архивации.

Проверка наличия в папке имён, длиной более 200 символов

При запуске программы в командной строке после имени запускаемой программы указывается имя исследуемой папки. Например, имя программы 114.exe, исследуется папка С:\Моно\Результат. Текст программы 114.cs:

 //Программа 114.cs.   Фиксация папок с длиной имени более  200 символов: 
using System; 
using System.IO;
class Class1 {
//Точка входа в программу [STAThread]
static void Main(string[]   args) {
string dir = args[0]; analiz(dir);
}
static void analiz(string dir)       //Анализ длины имён
{
Console.WriteLine("Исследуется папка    "  +  dir +  "\n");
string   []   subdirectory =
Directory.GetDirectories(dir,"*",System.IO.SearchOption.AllD irectories);
Console.WriteLine("Количество подкаталогов в папке       " + subdirectory.Length);
int k=0;
for   (int i = 0;   i < subdirectory.Length;     i++)
{
if(subdirectory[i].Length > 200)
{
Console.WriteLine(i  +  "    "  +  subdirectory[i]   +  " " +    subdirectory[i].Length); k+ = 1;
}
}
if(k==0)
{
Console.WriteLine("Папок с длиной имени более 200 символов нет ");
}
} 
}

 При отсутствии длинных имён:

А при наличии длинных имён выводятся порядковый номер каталога, путь к нему и длина имени:

Программа коррекции неправильных каталогов (со сверхдлинными путями)

Коррекция каталогов со сверхдлинными именами предусматривает:

  1. Создание в корне какого-либо диска рабочего каталога с именем "v".
  2. Размещение в этом каталоге необходимых для работы файлов: основного файла -архиватора rar.exe, вспомогательного файла архиватора - default.sfx, командного файла для запуска архиватора - comand.cmd.
  3. При моделировании реконструкции каталога требуется дополнительное мероприятие: создание каталога требуемой конструкции. Такой каталог может быть создан по адресу: с:\^Эксперимент\3. Этот каталог должен содержать иерархическую структуру подкаталогов с требуемой длиной и конструкцией имён.
  4. Программа remont.cs выполняет следующие действия:
    1. получает структуру каталогов исследуемой папки;
    2. перемещает каталог subdir (subdir - это переменная, содержащая текущее значение имени), длина имени которого превышает заданную величину, в рабочий каталог v и изменяет его имя на 1;
    3. запускает файл command.cmd, преобразующий каталог в sfx-архив (файл );
    4. перемещает созданный sfx-архив на место каталога subdir и изменяет его имя на subdir.exe.

Если каталог subdir имел в свою очередь иерархическую структуру, она обрывается, и таким образом сокращается общая длина имени.

 //Текст программы Remont2.cs
using System;
using System.IO;
using System.Diagnostics;
namespace RemontDir
{
class RemDir {
static void Main(string[] args)
{
string rab = @"c:\v\1";
string dir = @"С:\у\Эксперимент\3"; 
string [] subdirectory = Directory.GetDirectories(dir,"*" ,System.IO.SearchOption.AllDirectories);
foreach (string subdir in subdirectory)
{
if (subdir.Length > 200) {
Console. WriteLine("\n" + subdir + "  " + subdir.Length);
Directory.Move(subdir, rab); //Console.Read();
Process. Start( Vomand.cmd");
Directory.Move(@"c:\v\l.exe", subdir); //Console.Read();
}
}
}
}
}

Работа этой программы в автоматическом режиме на экране не отображается. Добавим в программу отладочные операторы, останавливающие её в контрольных точках. После остановки средствами операционной системы проанализируем состояние необходимых файлов и каталогов.

Фрагмент содержания папки c:\v перед началом работы программы remont2.exe:

Текст командного файла comand.cmd.

Результат анализа исследуемой папки программой 114.exe перед началом работы программы remont2.exe:

Структура исследуемой папки, полученная перед ремонтом по команде tree операционной системы:

Исполнение командного файла comand.cmd:

Содержимое диска v после работы архиватора:

Из рисунка видно, что на диске добавились папка "1" и sfx-архив 1.exe.

Проверка исследуемого каталога после преобразования по программе 114.exe:

Общее количество каталогов в папке уменьшилось, так как каталоги 10-14 являются подкаталогами каталога 9, имеющего размер имени больше 200 символов. Все они включены в архив 1.exe, что видно из протокола работы командного файла.