Difference between revisions of "Executing External Programs/ru"

From Free Pascal wiki
Jump to navigationJump to search
Line 14: Line 14:
  
 
== TProcess ==
 
== TProcess ==
You can use TProcess to launch external programs. Some of the benefits of using TProcess are that it is:
+
Вы можете использовать TProcess для запуска внешних программ. Самыми полезными вещами при этом будут:
  
*Platform Independent
+
*Платформонезависимость
*Capable of reading from stdout and writing to stdin.
+
*Способность читать из stdout и писать в stdin.
  
Note: TProcess is not a terminal/shell! You cannot directly execute scripts or redirect output using operators like "|", ">", "<", "&" etc. It is possible to obtain the same results with TProcess using pascal, some examples are below..
+
Примечание: TProcess не оболочка! И не терминал! Вы не можете напрямую исполнять скрипты или перенаправлять вывод используя такие операторы, как "|", ">", "<", "&" и т.д. Но возможно получить те же самые результаты используя TProcess, далее будут приведены некоторые примеры..
  
Important: You must specify the full path to the executable. For example '/bin/cp' instead of 'cp'. If the program is in the standard PATH then you can use the function [[doc:lcl/fileutil/finddefaultexecutablepath.html|FindDefaultExecutablePath]] from the [[doc:lcl/fileutil/index.html|FileUtil]] unit of the LCL.
+
Важно: Вы должны определять полный путь к исполняемому файлу. Например '/bin/cp' вместо 'cp'. Если программа находится где-либо в переменной PATH, то вы можете использовать функцию [[doc:lcl/fileutil/finddefaultexecutablepath.html|FindDefaultExecutablePath]] из модуля LCL [[doc:lcl/fileutil/index.html|FileUtil]].
  
=== A Simple Example ===
+
=== Простой пример ===
 
<pascal>
 
<pascal>
  // This is a demo program that shows how to launch
+
  // Демо-программа, показывающая, как можно запустить
  // an external program.
+
  // внешнюю программу
 
  program launchprogram;
 
  program launchprogram;
 
   
 
   
  // Here we include files that have useful functions
+
  // Подключаем модули с требуемыми
  // and procedures we will need.
+
  // нам процедурами и функциями.
 
  uses  
 
  uses  
 
   Classes, SysUtils, Process;
 
   Classes, SysUtils, Process;
 
   
 
   
  // This is defining the var "AProcess" as a variable
+
  // Опишем переменную "AProcess"  
  // of the type "TProcess"
+
  // типа "TProcess"
 
  var  
 
  var  
 
   AProcess: TProcess;
 
   AProcess: TProcess;
 
   
 
   
  // This is where our program starts to run
+
  // Здесь наша программа начинается
 
  begin
 
  begin
   // Now we will create the TProcess object, and
+
   // Создаем объект  TProcess и
   // assign it to the var AProcess.
+
   // присваиваем его переменной AProcess.
 
   AProcess := TProcess.Create(nil);
 
   AProcess := TProcess.Create(nil);
 
   
 
   
   // Tell the new AProcess what the command to execute is.
+
   // Сообщим AProcess сомандную строку для запуска
 
   // Let's use the FreePascal compiler
 
   // Let's use the FreePascal compiler
 
   AProcess.CommandLine := 'ppc386 -h';
 
   AProcess.CommandLine := 'ppc386 -h';
 
   
 
   
   // We will define an option for when the program
+
   // Необходимо описать опции программы для запуска
   // is run. This option will make sure that our program
+
   // Эта опция не позволит нашей программе выполнятся до тех пор, пока
   // does not continue until the program we will launch
+
   // запущенная программа не закончится
  // has stopped running.                vvvvvvvvvvvvvv
 
 
   AProcess.Options := AProcess.Options + [poWaitOnExit];
 
   AProcess.Options := AProcess.Options + [poWaitOnExit];
 
   
 
   
   // Now that AProcess knows what the commandline is
+
   // Теперь AProcess знает командную строку
   // we will run it.
+
   // и мы ее запускаем
 
   AProcess.Execute;
 
   AProcess.Execute;
 
   
 
   
   // This is not reached until ppc386 stops running.
+
   // Пока ppc386 не прекратит работу, мы досюда не дойдем
 
   AProcess.Free;   
 
   AProcess.Free;   
 
  end.
 
  end.
 
</pascal>
 
</pascal>
  
That's it! You have just learned how to run an external program from inside your own program.
+
Вот оно! Теперь вы научились запускать внешнюю программу изнутри вашей собственной.
  
=== An Improved Example ===
+
=== Усовершенствованный пример ===
That's nice, but how do I read the Output of a program that I have run?
+
Это все замечательно, но как я могу получить вывод программы, которую я запустил?
  
Well, let's expand our example a little and do just that:
+
Хорошо, пусть наш пример немного увеличится и теперь будет выглядеть так:
  
 
<delphi>
 
<delphi>
  // This is a demo program that shows how to launch
+
  // Это демо-программа, показывающая, как запускать
  // an external program and read from it's output.
+
  // внешнюю программу и читать ее вывод
 
  program launchprogram;
 
  program launchprogram;
 
   
 
   
  // Here we include files that have useful functions
+
  // Подключаем модули
// and procedures we will need.
 
 
  uses  
 
  uses  
 
   Classes, SysUtils, Process;
 
   Classes, SysUtils, Process;
 
   
 
   
  // This is defining the var "AProcess" as a variable
+
  // Опысываем переменную "AProcess"
  // of the type "TProcess"
+
  // И добавляем список строк TStringList для сбора данных
// Also now we are adding a TStringList to store the
+
  // из вывода программы
  // data read from the programs output.
 
 
  var  
 
  var  
 
   AProcess: TProcess;
 
   AProcess: TProcess;
 
   AStringList: TStringList;
 
   AStringList: TStringList;
 
   
 
   
  // This is where our program starts to run
+
  // Начинаем нашу программу
 
  begin
 
  begin
   // Now we will create the TProcess object, and
+
   // Создаем объект TProcess  
  // assign it to the var AProcess.
 
 
   AProcess := TProcess.Create(nil);
 
   AProcess := TProcess.Create(nil);
 
   
 
   
   // Create the TStringList object.
+
   // Создаем объект TStringList
 
   AStringList := TStringList.Create;
 
   AStringList := TStringList.Create;
 
   
 
   
   // Tell the new AProcess what the command to execute is.
+
   // Зададим командную строку
  // Let's use the FreePascal compiler
 
 
   AProcess.CommandLine := 'ppc386 -h';
 
   AProcess.CommandLine := 'ppc386 -h';
 
   
 
   
   // We will define an option for when the program
+
   // Установим опции программы. Первая из них не позволит нашей программе
  // is run. This option will make sure that our program
+
   // выполнятся до тех пор, пока не закончит выполнение запущенная программа
   // does not continue until the program we will launch
+
   // Также добавим опцию, которая говорит, что мы хотим прочитать
   // has stopped running. Also now we will tell it that
+
   // вывод запущенной программы
   // we want to read the output of the file.
 
 
   AProcess.Options := AProcess.Options + [poWaitOnExit, poUsePipes];
 
   AProcess.Options := AProcess.Options + [poWaitOnExit, poUsePipes];
 
   
 
   
   // Now that AProcess knows what the commandline is
+
   // Теперь запускаем программу
  // we will run it.
 
 
   AProcess.Execute;
 
   AProcess.Execute;
 
    
 
    
   // This is not reached until ppc386 stops running.
+
   // Пока запущенная программа не закончится, досюда мы не дойдем
 
   
 
   
   // Now read the output of the program we just ran
+
   // А теперь прочитаем вывод в список строк TStringList.
  // into the TStringList.
 
 
   AStringList.LoadFromStream(AProcess.Output);
 
   AStringList.LoadFromStream(AProcess.Output);
 
    
 
    
   // Save the output to a file.
+
   // Сохраним вывод в файл
 
   AStringList.SaveToFile('output.txt');
 
   AStringList.SaveToFile('output.txt');
 
   
 
   
   // Now that the file is saved we can free the
+
   // После сохранения файла мы можем уничтожить
   // TStringList and the TProcess.
+
   // TStringList и TProcess.
 
   AStringList.Free;
 
   AStringList.Free;
 
   AProcess.Free;   
 
   AProcess.Free;   

Revision as of 14:57, 26 May 2008

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)

Введение

Существует несколько путей редактирования, но данная статья описывает только один способ - 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.

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

<pascal>

// Демо-программа, показывающая, как можно запустить
// внешнюю программу
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.

</pascal>

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

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

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

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

<delphi>

// Это демо-программа, показывающая, как запускать
// внешнюю программу и читать ее вывод
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.

</delphi>

Reading large output

In the previous example we waited until the program exited. Then we read, what the program has written to its output. But suppose the program writes a lot of data to the output, the pipe becomes full and the called progam waits until the pipe has been read from. But the calling program doesn't read from it, until the called program has ended. A dead lock occurs.

The following example therefore doesn't use poWaitOnExit, but reads from the output, while the program is still running. The output is stored in a memory stream, that can be used later to read the output into a TStringList.

<delphi> program procoutlarge;

{
    Copyright (c) 2004 by Marc Weustink

    This example is creeated in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
}

uses
  Classes, Process, SysUtils;

const
  READ_BYTES = 2048;
  
var
  S: TStringList;
  M: TMemoryStream;
  P: TProcess;
  n: LongInt;
  BytesRead: LongInt;

begin
  // We cannot use poWaitOnExit here since we don't
  // know the size of the output. On Linux the size of the
  // output pipe is 2 kB. If the output data is more, we 
  // need to read the data. This isn't possible since we are 
  // waiting. So we get a deadlock here.
  //
  // A temp Memorystream is used to buffer the output
  
  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          
    // make sure we have room
    M.SetSize(BytesRead + READ_BYTES);
    
    // try reading it
    n := P.Output.Read((M.Memory + BytesRead)^, READ_BYTES);
    if n > 0 
    then begin
      Inc(BytesRead, n);
      Write('.')
    end
    else begin     
      // no data, wait 100 ms
      Sleep(100); 
    end;
  end;
  // read last part
  repeat
    // make sure we have room
    M.SetSize(BytesRead + READ_BYTES);
    // try reading it
    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.</delphi>

Using input and output of a TProcess

See processdemo example in the Lazarus-CCR SVN.

Hints on the use of TProcess

If you are creating a cross-platform program, you can change commandline according to the OS, using directives "{$IFDEF}s" and "{$ENDIF}s".

Example: <delphi> {...}

  AProcess:TProcess.Create(nil)
  {$IFDEF WIN32}
  AProcess.CommandLine := 'calc.exe'; //Windows Calc
  {$ENDIF}
  {$IFDEF LINUX}
  AProcess.CommandLine := 'kcalc'; //KDE Calc
  {$ENDIF}
  AProcess.Execute; //in alternative, you can use AProcess.Active:=True
{...}</delphi>

Example of "talking" with aspell process

Inside pasdoc source code you can find two units that perform spell-checking by "talking" with running aspell process through pipes:

  • PasDoc_ProcessLineTalk.pas unit implements TProcessLineTalk class, descendant of TProcess, that can be easily used to talk with any process on a line-by-line basis.
  • PasDoc_Aspell.pas units implements TAspellProcess class, that performs spell-checking by using underlying TProcessLineTalk instance to execute aspell and communicate with running aspell process.

Both units are rather independent from the rest of pasdoc sources, so they may serve as real-world examples of using TProcess to run and communicate through pipes with other program.