Executing External Programs/zh CN

From Lazarus wiki
Revision as of 08:58, 7 March 2019 by Robsean (talk | contribs) (贡献者和更改)

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

概述:对比

在RTL(运行时库)中有不同的可用的方法,FCL和LCL库,关于如何执行一个外部command(命 令)/process(进程)/program(程序).

Method Library Platforms Single Line Features
ExecuteProcess RTL 跨平台 非常有限,同时存在
ShellExecute WinAPI 仅微软Windows系统 很多。可以开始带有elevation/admin权限的程序。
fpsystem, fpexecve Unix 仅Unix系统
TProcess FCL 跨平台 完全
RunCommand FCL 跨平台 需要FPC 2.6.2+ 涉及共有的TProcess用法
OpenDocument LCL 跨平台 仅打开文档。文档可以使用相关联该文档类型的应用程序打开。

(Process.)RunCommand

在FPC 2.6.2中,对TProcess的一些有帮助的函数被添加到单元process,基于使用在fpcup项目的封装 (wrapper)。 这些函数为基础和中间物做准备,并且可以捕获输出到一个唯一的字符串,并完全支持大量输出的情况

一个简单的示例是

uses Process;
...
var s : ansistring;
...
if RunCommand('/bin/bash',['-c','echo $PATH'],s) then
   writeln(s);

RunCommand的一个超载(overloaded)变体,返回程序的退出代码。RunCommandInDir 在 一个不同的目录中运行命令(设置 p.CurrentDirectory):

function RunCommandIndir(const curdir:string;const exename:string;const commands:array of string;var outputstring:string; var exitstatus:integer): integer;
function RunCommandIndir(const curdir:string;const exename:string;const commands:array of string;var outputstring:string): boolean;
function RunCommand(const exename:string;const commands:array of string;var outputstring:string): boolean;

SysUtils.ExecuteProcess

(跨平台)
虽有一个数的限制,最简单的方法来启动一个程序(模态,无管道或一些控件的形式)是简单地使用 :

SysUtils.ExecuteProcess(UTF8ToSys('/full/path/to/binary'), '', []);

调用进程同步运行:它'挂起(hangs)',直到内部出现完成 - 但是,在你的程序中继续前,如果你想要用户来做 一些事,这可能是有用的。对于更多用途的方法,看下一关于优先考虑跨平台的TProcess部分, 或如果你仅希望瞄准Windows,你可以使用ShellExecute.

微软Windows系统  : CreateProcess, ShellExecute 和 WinExec

Template:注意 Template:注意

ShellExecute 是一个标准的微软Windows函数(ShellApi.h),在MSDN上有很好的文档(如果你发现函数不可靠,注意它们关于初始化COM的话语).

uses ..., ShellApi;

// Simple one-liner (ignoring error returns) :
if ShellExecute(0,nil, PChar('"C:\my dir\prog.exe"'),PChar('"C:\somepath\some_doc.ext"'),nil,1) =0 then;

// Execute a Batch File :
if ShellExecute(0,nil, PChar('cmd'),PChar('/c mybatch.bat'),nil,1) =0 then;

// Open a command window in a given folder :
if ShellExecute(0,nil, PChar('cmd'),PChar('/k cd \path'),nil,1) =0 then;

// Open a webpage URL in the default browser using 'start' command (via a brief hidden cmd window) :
if ShellExecute(0,nil, PChar('cmd'),PChar('/c start www.lazarus.freepascal.org/'),nil,0) =0 then;

// or a useful procedure:
procedure RunShellExecute(const prog,params:string);
begin
  //  ( Handle, nil/'open'/'edit'/'find'/'explore'/'print',   // 'open' isn't always needed 
  //      path+prog, params, working folder,
  //        0=hide / 1=SW_SHOWNORMAL / 3=max / 7=min)   // for SW_ constants : uses ... Windows ...
  if ShellExecute(0,'open',PChar(prog),PChar(params),PChar(extractfilepath(prog)),1) >32 then; //success
  // return values 0..32 are errors
end;

那儿也有ShellExecuteExW作为一个WideChar版本,并且ShellExecuteExA是 AnsiChar.

fMask选项也能使用SEE_MASK_DOENVSUBST或SEE_MASK_FLAG_NO_UI或 SEE_MASK_NOCLOSEPROCESS,等.

如果在Delphi中,你对文档使用ShellExecute,像Word文档,或URLs,看看 open* (openurl等等) 函数,在lclintf中 (看这页下面的可选择部分).

使用ShellExecuteEx对于elevation/administrator权限

如果你需要执行带有administrator/elevated权限的外部程序,你可以使用runas方 法替换ShellExecuteEx函数:

uses ShellApi, ...;

function RunAsAdmin(const Handle: Hwnd; const Path, Params: string): Boolean;
var
  sei: TShellExecuteInfoA;
begin
  FillChar(sei, SizeOf(sei), 0);
  sei.cbSize := SizeOf(sei);
  sei.Wnd := Handle;
  sei.fMask := SEE_MASK_FLAG_DDEWAIT or SEE_MASK_FLAG_NO_UI;
  sei.lpVerb := 'runas';
  sei.lpFile := PAnsiChar(Path);
  sei.lpParameters := PAnsiChar(Params);
  sei.nShow := SW_SHOWNORMAL;
  Result := ShellExecuteExA(@sei);
end;

procedure TFormMain.RunAddOrRemoveApplication;
begin
  // Example that uses elevated rundll to open the Control Panel to Programs and features
  RunAsAdmin(FormMain.Handle, 'rundll32.exe shell32.dll,Control_RunDLL appwiz.cpl', '');
end;

Unix系统 fpsystem, fpexecve和shell

这些函数依赖平台.

注意,1.0.x 'Unix.Shell 已经不赞成一段时间了,在trunk(主支)中被移除。使用psystem. .

TProcess

你可以使用TProcess来启动外部程序。使用TProcess的一些好处是:

  • 平台独立
  • 从stdout读和写到stdin的能力 .
  • 能做到等待一个命令完成或当你的程序移动时让它运行.

重要注释:

  • TTProcess不是一个terminal(控制台)/shell!你不能直接执行scripts(脚本),或使用运算符像"|", ">","<", "&"等等重定向输出,使用pascal的TProcess获得相同的结果 是可能的,一些示例在下面..
  • 很可能在Linux/Unix上:你must指定完整的路径到可执行文件。例如:'/bin/cp' 代替'cp'。如果程序是在标准的PATH(路径)上,那么你可以从LCL的单元FileUtil,使用函数FindDefaultExecutablePath.
  • 在Windows上,如果该命令是在路径中,你不需要指定完整的路径.
  • TProcess 引用

最简单的示例

很多的典型情况被事先准备在 Runcommand函数中。在你开始复制和粘贴下面的示例前,首先核查它们.

一个简单的示例

这个示例(这不能使用在生产中,看大量输出,或更好的, Runcommand)仅向你显示如何运行一个外部程序:

// This is a demo program that shows
// how to launch an external program.
program launchprogram;
 
// Here we include files that have useful functions
// and procedures we will need.
uses 
  Classes, SysUtils, Process;
 
// This defines the var "AProcess" as a variable 
// of the type "TProcess"
var 
  AProcess: TProcess;
 
// This is where our program starts to run
begin
  // Now we will create the TProcess object, and
  // assign it to the var AProcess.
  AProcess := TProcess.Create(nil);
 
  // Tell the new AProcess what the command to execute is.
  // Let's use the Free Pascal compiler (i386 version that is)
  AProcess.Executable:= 'ppc386';

  // Pass -h together with ppc386 so actually 'ppc386 -h' is executed:
  AProcess.Parameters.Add('-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];
 
  // Now let AProcess run the program
  AProcess.Execute;
 
  // This is not reached until ppc386 stops running.
  AProcess.Free;   
end.

就是这样,你刚刚学习从你的程序内部来运行一个外部程序.

一个提 高的示例(但是现在不正确)

这是好的,但是,我如何读一个我运行程序的输出?

好的,让我们稍微扩张我们的示例,并马上就要做这: 这个示例保持简单,因此你可以从它学习。请不要在产品代码中 使用这个示例,但是使用代码在 #读大量输出.

// This is a 
// FLAWED
// demo program that shows
// how to launch an external program
// and read from its output.
program launchprogram;
 
// Here we include files that have useful functions
// and procedures we will need.
uses 
  Classes, SysUtils, Process;
 
// This is defining the var "AProcess" as a variable 
// of the type "TProcess"
// Also now we are adding a TStringList to store the 
// data read from the programs output.
var 
  AProcess: TProcess;
  AStringList: TStringList;

// This is where our program starts to run
begin
  // Now we will create the TProcess object, and
  // assign it to the var AProcess.
  AProcess := TProcess.Create(nil);
 
  // Tell the new AProcess what the command to execute is.
  AProcess.Executable := '/usr/bin/ppc386'; 
  AProcess.Parameters.Add('-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];
 
  // Now that AProcess knows what the commandline is it can be run.
  AProcess.Execute;
  
  // After AProcess has finished, the rest of the program will be executed.
 
  // Now read the output of the program we just ran into a TStringList.
  AStringList := TStringList.Create;
  AStringList.LoadFromStream(AProcess.Output);
   
  // Save the output to a file and clean up the TStringList.
  AStringList.SaveToFile('output.txt');
  AStringList.Free;
 
  // Now that the output from the process is processed, it can be freed.
  AProcess.Free;   
end.

读大量输出

在前一个示例中,我们一直等到程序退出。然后,我们读什么程序写到它的输出。

假设程序写很多数据到输出。然后,输出管道变满,并且调用程序等待,直到管道被读取。

但是,调用程序不从它读,直到调用的程序被结束。一个停滞发生。

下面的示例因为不使用poWaitOnExit,但是,当出现仍然在运行时,从输出读取。输出被存储在内存流中,随后 可以被使用于读输出到一个TStringList中。

如果你想从一个外部进程中读输出,这是你需要改写到产品使用的代码.

program LargeOutputDemo;

{$mode objfpc}{$H+}

uses
  Classes, SysUtils, Process; // Process is the unit that holds TProcess

const
  BUF_SIZE = 2048; // Buffer size for reading the output in chunks

var
  AProcess     : TProcess;
  OutputStream : TStream;
  BytesRead    : longint;
  Buffer       : array[1..BUF_SIZE] of byte;

begin
  // Set up the process; as an example a recursive directory search is used
  // because that will usually result in a lot of data.
  AProcess := TProcess.Create(nil);

  // The commands for Windows and *nix are different hence the $IFDEFs
  {$IFDEF Windows}
    // In Windows the dir command cannot be used directly because it's a build-in
    // shell command. Therefore cmd.exe and the extra parameters are needed.
    AProcess.Executable := 'c:\windows\system32\cmd.exe';
    AProcess.Parameters.Add('/c');
    AProcess.Parameters.Add('dir /s c:\windows');
  {$ENDIF Windows}

  {$IFDEF Unix}
    AProcess.Executable := '/bin/ls';
    AProcess.Parameters.Add('--recursive');
    AProcess.Parameters.Add('--all');
    AProcess.Parameters.Add('-l');
  {$ENDIF Unix}

  // Process option poUsePipes has to be used so the output can be captured.
  // Process option poWaitOnExit can not be used because that would block
  // this program, preventing it from reading the output data of the process.
  AProcess.Options := [poUsePipes];

  // Start the process (run the dir/ls command)
  AProcess.Execute;

  // Create a stream object to store the generated output in. This could
  // also be a file stream to directly save the output to disk.
  OutputStream := TMemoryStream.Create;

  // All generated output from AProcess is read in a loop until no more data is available
  repeat
    // Get the new data from the process to a maximum of the buffer size that was allocated.
    // Note that all read(...) calls will block except for the last one, which returns 0 (zero).
    BytesRead := AProcess.Output.Read(Buffer, BUF_SIZE);

    // Add the bytes that were read to the stream for later usage
    OutputStream.Write(Buffer, BytesRead)

  until BytesRead = 0;  // Stop if no more data is available

  // The process has finished so it can be cleaned up
  AProcess.Free;

  // Now that all data has been read it can be used; for example to save it to a file on disk
  with TFileStream.Create('output.txt', fmCreate) do
  begin
    OutputStream.Position := 0; // Required to make sure all data is copied from the start
    CopyFrom(OutputStream, OutputStream.Size);
    Free
  end;

  // Or the data can be shown on screen
  with TStringList.Create do
  begin
    OutputStream.Position := 0; // Required to make sure all data is copied from the start
    LoadFromStream(OutputStream);
    writeln(Text);
    writeln('--- Number of lines = ', Count, '----');
    Free
  end;

  // Clean up
  OutputStream.Free;
end.

注意,上面应使用RunCommand完成:

var s: string;
...
RunCommand('c:\windows\system32\cmd.exe', ['/c', 'dir /s c:\windows'], s);

使用一个TProcess的 输入和输出

查看processdemo示例,在Lazarus-CCR SVN.

关于TProcess的使用 的提示

当创建一个跨平台程序,指定的操作系统可执行文件名称可以使用指令"{$IFDEF}"和{$ENDIF}"设置.

示例:

{...}
AProcess := TProcess.Create(nil)

{$IFDEF WIN32}
  AProcess.Executable := 'calc.exe'; 
{$ENDIF}

{$IFDEF LINUX}
  AProcess.Executable := FindDefaultExecutablePath('kcalc');
{$ENDIF}

AProcess.Execute;
{...}

OS X 在前台显示应用程序包

你可以凭借TProcess通过在软件包中启动可执行文件开始一个应用程序包. 例如:

 AProcess.Executable:='/Applications/iCal.app/Contents/MacOS/iCal';

这将开始Calendar,但是窗口讲在当前应用程序的后面。为在前台获取应用程序,你可以使用 open能,带有-n参数:

 AProcess.Executable:='/usr/bin/open';
 AProcess.Parameters.Add('-n');
 AProcess.Parameters.Add('-a'); //optional: to hide terminal - similar to Windows option poNoConsole
 AProcess.Parameters.Add('/Application/iCal.app');

如果你的应用程序需要参数,你可以传送open程序的--args参数,在所有的参赛被传送到应用程序后:

 AProcess.Parameters.Add('--args');
 AProcess.Parameters.Add('argument1');
 AProcess.Parameters.Add('argument2');

运行独立的程序

一般的,由你的应用程序开始的程序是一个子进程,当你的应用程序被杀死时,亦被杀死。.当你想运行 一个独立的保持运行的程序,你可以使用下面:

var
  Process: TProcess;
  I: Integer;
begin
  Process := TProcess.Create(nil);
  try
    Process.InheritHandles := False;
    Process.Options := [];
    Process.ShowWindow := swoShow;

    // Copy default environment variables including DISPLAY variable for GUI application to work
    for I := 1 to GetEnvironmentVariableCount do
      Process.Environment.Add(GetEnvironmentString(I));

    Process.Executable := '/usr/bin/gedit';  
    Process.Execute;
  finally
    Process.Free;
  end;
end;

"讨论" 带有aspell进程的示例

pasdoc源 文件代码里面,你可以找到两个单元,这通过管道(pipes)经由"讨论"带有aspell进程的执行(perform) 拼写检查:

  • PasDoc_ProcessLineTalk.pas unit实施TProcessLineTalk类,TProcess的衍生物,这可以被简单地用于讨论一些关于逐行基础(basis) 的进程.
  • PasDoc_Aspell.pas units实施TAspellProcess类,这履行拼写检查,通过使用下层的TProcessLineTalk示例老执行aspell和与运行的aspell进 程取得联系.

这两个单元都与pasdoc源文件的剩余部分相当独立,因此,它们可以充当使用TProcess来运行并通过管道与其 它程序通讯的真实的示例.

替换shell运算符,像 "| < >"

有时,你想运行更复杂的命令,命令传输它的数据到另一个命令或文件。一些东西看起来

ShellExecute('firstcommand.exe | secondcommand.exe');

ShellExecute('dir > output.txt');

使用TProcess执行这个将被不工作,例如:

// this won't work
Process.CommandLine := 'firstcommand.exe | secondcommand.exe'; 
Process.Execute;

为什么使用指定运算符到重定向输出不工作

TProcess仅仅是这,它不是一个shell环境,仅是一个进程process(进程)。它不是两个进程,它仅是 一个。无论怎样你想要方法的重定向输出是运行的。看下一部分.

如何使用TProcess 重定向输出

你可以为每一个命令使用一个TProcess实例重定向一个命令的输出到另一个命令.

这是一个示例,解释重定向一个进程的输出到另一个。为重定向一个进程的输出到一个文件/流,看示例 读大量输出

不仅你可以重定向"正常"输出(也被称为stdout),但是你也可以重定向错误输出(stderr),如果你指定 poStderrToOutPut选项,像在第二个进程选项中所看.

program Project1;
  
uses
  Classes, sysutils, process;
  
var
  FirstProcess,
  SecondProcess: TProcess;
  Buffer: array[0..127] of char;
  ReadCount: Integer;
  ReadSize: Integer;
begin
  FirstProcess  := TProcess.Create(nil);
  SecondProcess := TProcess.Create(nil);
 
  FirstProcess.Options     := [poUsePipes]; 
  FirstProcess.Executable  := 'pwd'; 
  
  SecondProcess.Options    := [poUsePipes,poStderrToOutPut];
  SecondProcess.Executable := 'grep'; 
  SecondProcess.Parameters.Add(DirectorySeparator+ ' -'); 
  // this would be the same as "pwd | grep / -"
  
  FirstProcess.Execute;
  SecondProcess.Execute;
  
  while FirstProcess.Running or (FirstProcess.Output.NumBytesAvailable > 0) do
  begin
    if FirstProcess.Output.NumBytesAvailable > 0 then
    begin
      // make sure that we don't read more data than we have allocated
      // in the buffer
      ReadSize := FirstProcess.Output.NumBytesAvailable;
      if ReadSize > SizeOf(Buffer) then
        ReadSize := SizeOf(Buffer);
      // now read the output into the buffer
      ReadCount := FirstProcess.Output.Read(Buffer[0], ReadSize);
      // and write the buffer to the second process
      SecondProcess.Input.Write(Buffer[0], ReadCount);
  
      // if SecondProcess writes much data to it's Output then 
      // we should read that data here to prevent a deadlock
      // see the previous example "Reading Large Output"
    end;
  end;
  // Close the input on the SecondProcess
  // so it finishes processing it's data
  SecondProcess.CloseInput;
 
  // and wait for it to complete
  // be carefull what command you run because it may not exit when
  // it's input is closed and the following line may loop forever
  while SecondProcess.Running do
    Sleep(1);
  // that's it! the rest of the program is just so the example
  // is a little 'useful'

  // we will reuse Buffer to output the SecondProcess's
  // output to *this* programs stdout
  WriteLn('Grep output Start:');
  ReadSize := SecondProcess.Output.NumBytesAvailable;
  if ReadSize > SizeOf(Buffer) then
    ReadSize := SizeOf(Buffer);
  if ReadSize > 0 then
  begin
    ReadCount := SecondProcess.Output.Read(Buffer, ReadSize);
    WriteLn(Copy(Buffer,0, ReadCount));
  end
  else
    WriteLn('grep did not find what we searched for. ', SecondProcess.ExitStatus);
  WriteLn('Grep output Finish:');
  
  // free our process objects
  FirstProcess.Free;
  SecondProcess.Free;
end.

这是全部。现在你可以从一个程序重定向输出到另一个.

注意

这个示例可能看着太夸张,尽管它能使用一个shell带有TProcess来运行"结构复杂的"命令,像:

Process.Commandline := 'sh -c "pwd | grep / -"';

但是我们的示例是更跨平台的,尽管它在Windows或Linuc等等上不需要修改. "sh"可能或不可能存在在你的 平台上,一般是仅在*nix平台上。另外,在我们的示例中,我们有更多的灵活性,尽管你可以各自地读和写从/到输入,输 出和每个标准错误(stderr),这对你的工程可能非常有利.

在 root下,重定向输入和输出,并运行

在Unixes (OSX)和Linux上的一个一般问题是,你想在root账户(或,更一般地,其他的用户账户)下执行一些程序。一个示例将运行 ping 命令.

如果你可以对这使用sudo。你可以改写(adapt)下面改写自andman张贴在论坛 ([1]) 上的示例. 这个示例在 /root 目录运行ls, 但是,当然可以被改写.

A better way to do this is to use the policykit package, which should be available on all recent Linuxes. See the forum thread for details.

Large parts of this code are similar to the earlier example, but it also shows how to redirect stdout and stderr of the process being called separately to stdout and stderr of our own code.

program rootls;

{ Demonstrates using TProcess, redirecting stdout/stderr to our stdout/stderr,
calling sudo on Linux/OSX, and supplying input on stdin}
{$mode objfpc}{$H+}

uses
  Classes,
  Math, {for min}
  Process;

  procedure RunsLsRoot;
  var
    Proc: TProcess;
    CharBuffer: array [0..511] of char;
    ReadCount: integer;
    ExitCode: integer;
    SudoPassword: string;
  begin
    WriteLn('Please enter the sudo password:');
    Readln(SudoPassword);
    ExitCode := -1; //Start out with failure, let's see later if it works
    Proc := TProcess.Create(nil); //Create a new process
    try
      Proc.Options := [poUsePipes, poStderrToOutPut]; //Use pipes to redirect program stdin,stdout,stderr
      Proc.CommandLine := 'sudo -S ls /root'; //Run ls /root as root using sudo
      // -S causes sudo to read the password from stdin.
      Proc.Execute; //start it. sudo will now probably ask for a password

      // write the password to stdin of the sudo program:
      SudoPassword := SudoPassword + LineEnding;
      Proc.Input.Write(SudoPassword[1], Length(SudoPassword));
      SudoPassword := '%*'; //short string, hope this will scramble memory a bit; note: using PChars is more fool-proof
      SudoPassword := ''; // and make the program a bit safer from snooping?!?

      // main loop to read output from stdout and stderr of sudo
      while Proc.Running or (Proc.Output.NumBytesAvailable > 0) or
        (Proc.Stderr.NumBytesAvailable > 0) do
      begin
        // read stdout and write to our stdout
        while Proc.Output.NumBytesAvailable > 0 do
        begin
          ReadCount := Min(512, Proc.Output.NumBytesAvailable); //Read up to buffer, not more
          Proc.Output.Read(CharBuffer, ReadCount);
          Write(StdOut, Copy(CharBuffer, 0, ReadCount));
        end;
        // read stderr and write to our stderr
        while Proc.Stderr.NumBytesAvailable > 0 do
        begin
          ReadCount := Min(512, Proc.Stderr.NumBytesAvailable); //Read up to buffer, not more
          Proc.Stderr.Read(CharBuffer, ReadCount);
          Write(StdErr, Copy(CharBuffer, 0, ReadCount));
        end;
      end;
      ExitCode := Proc.ExitStatus;
    finally
      Proc.Free;
      Halt(ExitCode);
    end;
  end;

begin
  RunsLsRoot;
end.

Other thoughts: It would no doubt be advisable to see if sudo actually prompts for a password. This can be checked consistently by setting the environment variable SUDO_PROMPT to something we watch for while reading the stdout of TProcess avoiding the problem of the prompt being different for different locales. Setting an environment variable causes the default values to be cleared(inherited from our process) so we have to copy the environment from our program if needed.

在Linux上带有sudo使用 fdisk

下面的示例显示在一个Linux机器上如何使用sudo命令获取root权限来运行fdisk. 注意:这仅是一 个示例,并不适合大量输出.

program getpartitioninfo;
{Originally contributed by Lazarus forums wjackon153. Please contact him for questions, remarks etc.
Modified from Lazarus snippet to FPC program for ease of understanding/conciseness by BigChimp}

Uses
  Classes, SysUtils, FileUtil, Process;

var
  hprocess: TProcess;
  sPass: String;
  OutputLines: TStringList;

begin  
  sPass := 'yoursudopasswordhere'; // You need to change this to your own sudo password
  OutputLines:=TStringList.Create; //... a try...finally block would be nice to make sure 
  // OutputLines is freed... Same for hProcess.
     
  // The following example will open fdisk in the background and give us partition information
  // Since fdisk requires elevated priviledges we need to 
  // pass our password as a parameter to sudo using the -S
  // option, so it will will wait till our program sends our password to the sudo application
  hProcess := TProcess.Create(nil);
  // On Linux/Unix/OSX, we need specify full path to our executable:
  hProcess.Executable := '/bin/sh';
  // Now we add all the parameters on the command line:
  hprocess.Parameters.Add('-c');
  // Here we pipe the password to the sudo command which then executes fdisk -l: 
  hprocess.Parameters.add('echo ' + sPass  + ' | sudo -S fdisk -l');
  // Run asynchronously (wait for process to exit) and use pipes so we can read the output pipe
  hProcess.Options := hProcess.Options + [poWaitOnExit, poUsePipes];
  // Now run:
  hProcess.Execute;

  // hProcess should have now run the external executable (because we use poWaitOnExit).
  // Now you can process the process output (standard output and standard error), eg:
  OutputLines.Add('stdout:');
  OutputLines.LoadFromStream(hprocess.Output);
  OutputLines.Add('stderr:');
  OutputLines.LoadFromStream(hProcess.Stderr);
  // Show output on screen:
  writeln(OutputLines.Text);

  // Clean up to avoid memory leaks:
  hProcess.Free;
  OutputLines.Free;
  
  //Below are some examples as you see we can pass illegal characters just as if done from terminal 
  //Even though you have read elsewhere that you can not I assure with this method you can :)

  //hprocess.Parameters.Add('ping -c 1 www.google.com');
  //hprocess.Parameters.Add('ifconfig wlan0 | grep ' +  QuotedStr('inet addr:') + ' | cut -d: -f2');

  //Using QuotedStr() is not a requirement though it makes for cleaner code;
  //you can use double quote and have the same effect.

  //hprocess.Parameters.Add('glxinfo | grep direct');   

  // This method can also be used for installing applications from your repository:

  //hprocess.Parameters.add('echo ' + sPass  + ' | sudo -S apt-get install -y pkg-name'); 

 end.

参数含有空格(替换Shell 引用)

在Linux shell中,写引用参数是可接受的,像这:

gdb --batch --eval-command="info symbol 0x0000DDDD" myprogram

并且GDB将接受3个参数(除第一个参数外,它是完整的路径到可执行文件):

  1. --batch
  2. --eval-command=info symbol 0x0000DDDD
  3. 完整的路径到myprogram

TProcess也可以传递保护空格的参数,但是它使用一个不同的引用样式。而不是仅引用参数的部分,引用参数的全 部。像这样:

TProcess.CommandLine := '/usr/bin/gdb --batch "--eval-command=info symbol 0x0000DDDD" /home/me/myprogram';

并且也记住仅传递完整的路径.

参考关于它的这个讨论: http://bugs.freepascal.org/view.php?id=14446

LCLIntf替代方案

有时,你不需要明确地调用一个外部程序来获取你需要的功能。作为打开一个应用程序和指定的文 件来跟它相配的代替,仅要求OS来打开文档和让它使用与这些文件类型相关联的默认应用程序、下面是一些示例.

打开文档在默认应用程序中

在一些情况下,你需要使用默认相关联的应用程序打开一下文档/文件,而不是执行一个独有的程序。这依赖于运行的操作系统。Lazarus通过一个平台独立procedure(过程)OpenDocument,这将为你处理它。你的应用程序将持续运行,而不等待文档进程关闭.

uses LCLIntf;
...
OpenDocument('manual.pdf');  
...

在默认网页浏览器中打开网页

J只传递需要的URL,在某些情况下,前列的http://似乎是可选的。并且,传递一个文件名称似乎与给予OpenDocument()的结果相同.

uses LCLIntf;
...
OpenURL('www.lazarus.freepascal.org/');

请参阅:

或,你可以使用 TProcess 像这:

uses Process;

procedure OpenWebPage(URL: string);
// Apparently you need to pass your URL inside ", like "www.lazarus.freepascal.org"
var
  Browser, Params: string;
begin
  FindDefaultBrowser(Browser, Params);
  with TProcess.Create(nil) do
  try
    Executable := Browser;
    Params:=Format(Params, [URL]);
    Params:=copy(Params,2,length(Params)-2); // remove "", the new version of TProcess.Parameters does that itself
    Parameters.Add(Params);
    Options := [poNoConsole];
    Execute;
  finally
    Free;
  end;
end;

请参阅

贡献者和更改

  • 简体中文版本由 robsean 于 2019-03-07 创建。