Difference between revisions of "Executing External Programs/ru"

From Free Pascal wiki
Jump to navigationJump to search
(→‎Введение: added info from english page adn translation)
Line 1: Line 1:
 
{{Executing External Programs}}
 
{{Executing External Programs}}
  
== Введение ==
+
== Введение: chfytybt ==
 +
 
 +
В библиотеках RTL, FCL, LCL есть разные способы выполнить внешнюю программу.
 +
{| class="wikitable"
 +
!Метод
 +
!Библиотека
 +
!Платформы
 +
!Одной строкой?
 +
!Особенности
 +
|-
 +
|[[Executing External Programs#SysUtils.ExecuteProcess|ExecuteProcess]]
 +
|RTL
 +
|Cross-Platform
 +
|Да
 +
|Очень ограничен, синхронен
 +
|-
 +
|[[Executing External Programs#MS Windows : CreateProcess, ShellExecute and WinExec|ShellExecute]]
 +
|WinAPI
 +
|Только MS Windows
 +
|Да
 +
|Много. Может запускать программы с правами администратора
 +
|-
 +
|[[Executing External Programs#Unix fpsystem, fpexecve and shell |fpsystem, fpexecve]]
 +
|Unix
 +
|Unix only
 +
|
 +
|
 +
|-
 +
|[[Executing External Programs#TProcess|TProcess]]
 +
|FCL
 +
|Cross-Platform
 +
|Нет
 +
|Все
 +
|-
 +
|[[Executing External Programs#(Process.)RunCommand|RunCommand]]
 +
|FCL
 +
|Cross-Platform '''Requires FPC 2.6.2+'''
 +
|Yes
 +
|Covers common TProcess usage
 +
|-
 +
|[[Executing External Programs#LCLIntf Alternatives|OpenDocument]]
 +
|LCL
 +
|Cross-Platform
 +
|Yes
 +
|Только открывает документ. Документ будет открыт стандартной программой
 +
|}
 +
 
  
 
Существует несколько способов запуска внешних программ, но данная статья описывает только один способ - [[doc:fcl/process/tprocess.html|TProcess]].
 
Существует несколько способов запуска внешних программ, но данная статья описывает только один способ - [[doc:fcl/process/tprocess.html|TProcess]].

Revision as of 21:00, 25 January 2016

Deutsch (de) English (en) español (es) français (fr) italiano (it) 日本語 (ja) Nederlands (nl) polski (pl) português (pt) русский (ru) slovenčina (sk) 中文(中国大陆)‎ (zh_CN)

Введение: chfytybt

В библиотеках RTL, FCL, LCL есть разные способы выполнить внешнюю программу.

Метод Библиотека Платформы Одной строкой? Особенности
ExecuteProcess RTL Cross-Platform Да Очень ограничен, синхронен
ShellExecute WinAPI Только MS Windows Да Много. Может запускать программы с правами администратора
fpsystem, fpexecve Unix Unix only
TProcess FCL Cross-Platform Нет Все
RunCommand FCL Cross-Platform Requires FPC 2.6.2+ Yes Covers common TProcess usage
OpenDocument LCL Cross-Platform Yes Только открывает документ. Документ будет открыт стандартной программой


Существует несколько способов запуска внешних программ, но данная статья описывает только один способ - TProcess.

Если вы использовали ShellExecute и/или WinExec в Delphi, то вы можете начать использовать TProcess как альтернативу в FPC/Lazarus (это верно и в случае использования Lazarus на Linux, потому что TProcess является кроссплатформенным компонентом).

Примечание: FPC/Lazarus поддерживает ShellExecute и WinExec, но только в среде Win32. Если вы пишете кросс-платформенную программу, то лучшим путем будет использование TProcess!

SysUtils.ExecuteProcess

Простейший путь, если вам не нужно общение с процессом - это просто использовать вызов SysUtils.ExecuteProcess('/full/path/to/binary',['arg1','arg2']);

TProcess

Вы можете использовать TProcess для запуска внешних программ. Самыми полезными вещами при этом будут:

  • Платформонезависимость
  • Способность читать из stdout и писать в stdin.

Примечание: TProcess не оболочка! И не терминал! Вы не можете напрямую исполнять скрипты или перенаправлять вывод используя такие операторы, как "|", ">", "<", "&" и т.д. Но возможно получить те же самые результаты используя TProcess, далее будут приведены некоторые примеры..

Важно: Вы должны определять полный путь к исполняемому файлу. Например '/bin/cp' вместо 'cp'. Если программа находится где-либо в переменной PATH, то вы можете использовать функцию FindDefaultExecutablePath из модуля LCL FileUtil.

Простой пример

 // Демо-программа, показывающая, как можно запустить
 // внешнюю программу
 program launchprogram;
 
 // Подключаем модули с требуемыми
 // нам процедурами и функциями.
 uses 
   Classes, SysUtils, Process;
 
 // Опишем переменную "AProcess" 
 // типа "TProcess"
 var 
   AProcess: TProcess;
 
 // Здесь наша программа начинается
 begin
   // Создаем объект  TProcess и
   // присваиваем его переменной AProcess.
   AProcess := TProcess.Create(nil);
 
   // Сообщим AProcess сомандную строку для запуска
   // Let's use the FreePascal compiler
   AProcess.CommandLine := 'ppc386 -h';
 
   // Необходимо описать опции программы для запуска
   // Эта опция не позволит нашей программе выполнятся до тех пор, пока 
   // запущенная программа не закончится
   AProcess.Options := AProcess.Options + [poWaitOnExit];
 
   // Теперь AProcess знает командную строку
   // и мы ее запускаем
   AProcess.Execute;
 
   // Пока ppc386 не прекратит работу, мы досюда не дойдем
   AProcess.Free;   
 end.

Вот оно! Теперь вы научились запускать внешнюю программу изнутри вашей собственной.

Усовершенствованный пример

Это все замечательно, но как я могу получить вывод программы, которую я запустил?

Хорошо, пусть наш пример немного увеличится и теперь будет выглядеть так:

 // Это демо-программа, показывающая, как запускать
 // внешнюю программу и читать ее вывод
 program launchprogram;
 
 // Подключаем модули
 uses 
   Classes, SysUtils, Process;
 
 // Опысываем переменную "AProcess"
 // И добавляем список строк TStringList для сбора данных
 // из вывода программы
 var 
   AProcess: TProcess;
   AStringList: TStringList;
 
 // Начинаем нашу программу
 begin
   // Создаем объект TProcess 
   AProcess := TProcess.Create(nil);
 
   // Создаем объект TStringList
   AStringList := TStringList.Create;
 
   // Зададим командную строку
   AProcess.CommandLine := 'ppc386 -h';
 
   // Установим опции программы. Первая из них не позволит нашей программе
   // выполнятся до тех пор, пока не закончит выполнение запущенная программа
   // Также добавим опцию, которая говорит, что мы хотим прочитать 
   // вывод запущенной программы
   AProcess.Options := AProcess.Options + [poWaitOnExit, poUsePipes];
 
   // Теперь запускаем программу
   AProcess.Execute;
   
   // Пока запущенная программа не закончится, досюда мы не дойдем
 
   // А теперь прочитаем вывод в список строк TStringList.
   AStringList.LoadFromStream(AProcess.Output);
   
   // Сохраним вывод в файл
   AStringList.SaveToFile('output.txt');
 
   // После сохранения файла мы можем уничтожить
   // TStringList и TProcess.
   AStringList.Free;
   AProcess.Free;   
 end.

Чтение больших объемов вывода

В предыдущем примере мы ждали завершения запущенной программы. После этого мы считывали все, что программа записала в выходной поток. Но ведь может оказаться и так, что программа выведет много данных, канал заполнится и вывод остановится, при этом запустившая программа ждет завершения запущенной программы, которая в свою очередь не может завершить работу, пока не выведет все данные. Возникает коллизия, dead-lock.

Поэтому следующий пример не будет использовать опцию poWaitOnExit и будет читать данные при запущенной программе. Вывод записывается в поток, который можно позже использовать для чтения его содержимого в TStringList.

 program procoutlarge;
 {
     Copyright (c) 2004 by Marc Weustink
 
     Этот пример был создан в надежде быть вам полезным,
     но без каких-либо подразумеваемых или явных  гарантий; 
 }
 
 uses
   Classes, Process, SysUtils;
 
 const
   READ_BYTES = 2048;
   
 var
   S: TStringList;
   M: TMemoryStream;
   P: TProcess;
   n: LongInt;
   BytesRead: LongInt;
 
 begin
   // Мы не можем использовать poWaitOnExit не зная размер вывода
   // В Linux размер выходного канала равен 2 kB. Если размер выводных
   // данных больше, то мы должны считывать данные. 
   // Пока мы ждем, чтение невозможно. Соответственно, мы получаем 
   // коллизию.
   //
   // Используем для буфера временный  поток Memorystream 
   
   M := TMemoryStream.Create;
   BytesRead := 0;
 
   P := TProcess.Create(nil);
   P.CommandLine := 'ppc386 -va bogus.pp';
   P.Options := [poUsePipes];
   WriteLn('-- executing --');
   P.Execute;
   while P.Running do
   begin          
     // Убедимся, что нам хватит места
     M.SetSize(BytesRead + READ_BYTES);
     
     // попытаемся прочитать данные
     n := P.Output.Read((M.Memory + BytesRead)^, READ_BYTES);
     if n > 0 
     then begin
       Inc(BytesRead, n);
       Write('.')
     end
     else begin     
       // нет данных, ждем 100 ms
       Sleep(100); 
     end;
   end;
   // читаем последний блок
   repeat
     // убедимся, что хватает места
     M.SetSize(BytesRead + READ_BYTES);
     // пытаемся прочитать
     n := P.Output.Read((M.Memory + BytesRead)^, READ_BYTES);
     if n > 0 
     then begin
       Inc(BytesRead, n);
       Write('.');
     end;
   until n <= 0;
   if BytesRead > 0 then WriteLn;
   M.SetSize(BytesRead); 
   WriteLn('-- executed --');
   
   S := TStringList.Create;
   S.LoadFromStream(M);
   WriteLn('-- linecount = ', S.Count, ' --');
   for n := 0 to S.Count - 1 do
   begin
     WriteLn('| ', S[n]);
   end;
   WriteLn('-- end --');
   S.Free;
   P.Free;
   M.Free;
 end.

Использование ввода и вывода TProcess

Смотри демо пример на Lazarus-CCR SVN.

Некоторые подсказки при использовании TProcess

Если вы создаете кроссплатформенную программу, вы можете изменять командную строку применительно к каждой ОС использую директивы "{$IFDEF}" и "{$ENDIF}".

Например:

 {...}
   AProcess:TProcess.Create(nil)
   {$IFDEF WIN32}
   AProcess.CommandLine := 'calc.exe'; //Windows калькулятор
   {$ENDIF}
   {$IFDEF LINUX}
   AProcess.CommandLine := 'kcalc'; //KDE калькулятор
   {$ENDIF}
   AProcess.Execute; //как альтернативу, вы можете использовать AProcess.Active:=True
 {...}

Пример "общения" с процессом aspell

Внутри исходного кода pasdoc расположены два модуля, выполняющие проверку орфографии, "общаясь" между собой посредством передачи управления процессу в котором они выполняются:

  • PasDoc_ProcessLineTalk.pas - Данный модуль реализует класс TProcessLineTalk являющегося потомком TProcess. Он может быть использован для обмена данными с любым процессом на его основе.
  • PasDoc_Aspell.pas - Реализует класс TAspellProcess, который выполняет проверку орфографии и наследуется от TProcessLineTalk, для выполнения aspell и обращения к запустившему его процессу.

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