Difference between revisions of "Executing External Programs/es"

From Free Pascal wiki
Jump to navigationJump to search
(Enlace a la propia página)
 
(48 intermediate revisions by 8 users not shown)
Line 8: Line 8:
  
 
   '''Nota:''' FPC/Lazarus puede utilizar '''ShellExecute''' y/o '''WinExec''', pero sólo en Win32. Si tu programa está enfocado a varias plataformas, utiliza TProcess, ¡es mejor!.
 
   '''Nota:''' FPC/Lazarus puede utilizar '''ShellExecute''' y/o '''WinExec''', pero sólo en Win32. Si tu programa está enfocado a varias plataformas, utiliza TProcess, ¡es mejor!.
 +
 +
 +
Aquí tenemos las diferentes maneras disponibles sobre como ejecutar un programa/proceso/mandato externo en RTL, FCL y LCL.
 +
 +
{| class="wikitable"
 +
!Metodo
 +
!Librería
 +
!Plataformas
 +
!Línea simple
 +
!Características
 +
|-
 +
|[[Executing External Programs#SysUtils.ExecuteProcess|ExecuteProcess]]
 +
|RTL
 +
|Plataforma Cruzada
 +
|Si
 +
|Muy limitado, synchronous
 +
|-
 +
|[[Executing External Programs#MS Windows : CreateProcess, ShellExecute and WinExec|ShellExecute]]
 +
|WinAPI
 +
|MS Windows only
 +
|Yes
 +
|Muchos. Permite iniciar programas con permisos de elevación/admin.
 +
|-
 +
|[[Executing External Programs#Unix fpsystem, fpexecve and shell |fpsystem, fpexecve]]
 +
|Unix
 +
|Unix solamente
 +
|
 +
|
 +
|-
 +
|[[Executing External Programs/es#TProcess|TProcess]]
 +
|FCL
 +
|Plataforma cruzada
 +
|No
 +
|Completas
 +
|-
 +
|[[Executing External Programs#(Process.)RunCommand|RunCommand]]
 +
|FCL
 +
|Plataforma cruzada, '''Requiere FPC 2.6.2+'''
 +
|Si
 +
|Cubre el uso común de TProcess
 +
|-
 +
|[[Executing External Programs#LCLIntf Alternatives|OpenDocument]]
 +
|LCL
 +
|Plataforma cruzada
 +
|si
 +
|Solo para abrir documentos. El documento debe abrirse con la aplicación asociada al tipo de documento.
 +
|}
  
 
== TProcess ==
 
== TProcess ==
Line 14: Line 61:
 
*Independiente de la plataforma
 
*Independiente de la plataforma
 
*Capaz de leer desde la entrada estándar (''stdout'') y de escribir en la salida estándar (''stdin'').
 
*Capaz de leer desde la entrada estándar (''stdout'') y de escribir en la salida estándar (''stdin'').
 +
 +
&nbsp;&nbsp;&nbsp;'''Nota''': TProcess no es un terminal de shell! No puedes ejecutar directamente secuencias de comandos o reorientar la salida usando operadores como "|", ">", "<", "&", etc. Es posible obtener los mismos resultados con TProcess utilizando Pascal, mira algunos ejemplos más abajo..
 +
 +
&nbsp;&nbsp;&nbsp;'''Importante''': Debes especificar la ruta completa al ejecutable. Por ejemplo "/bin/cp " en lugar de "cp". Si el programa está en la ruta estándar, puedes utilizar la funci&oacute;n '''FindDefaultExecutablePath''' de la unidad '''FileUtil''' de la LCL.
  
 
=== Un ejemplo sencillo ===
 
=== Un ejemplo sencillo ===
<delphi> // Este es un programa de demostración que enseña como lanzar
+
<syntaxhighlight lang=pascal>
 +
// Este es un programa de demostración que enseña como lanzar
 
  // un programa externo.
 
  // un programa externo.
  program launchprogram;
+
  program lanzarprograma;
 
   
 
   
 
  uses  
 
  uses  
Line 29: Line 81:
  
 
  begin
 
  begin
   // Ahora se creamos un objeto TProcess, y
+
   // Ahora creamos UnProceso.
  // lo asignamos a la variable UnProceso.
 
 
   UnProceso := TProcess.Create(nil);
 
   UnProceso := TProcess.Create(nil);
 
   
 
   
   // Asugnamos a UnProceso la orden que debe ejecutar.
+
   // Asignamos a UnProceso la orden que debe ejecutar.
   // Vamos a lanzazar el compilador de FreePascal  
+
   // Vamos a lanzar el compilador de FreePascal  
 
   UnProceso.CommandLine := 'ppc386 -h';
 
   UnProceso.CommandLine := 'ppc386 -h';
 
   
 
   
 
   // Definimos una opción de comportamiento de 'TProccess'
 
   // Definimos una opción de comportamiento de 'TProccess'
   // Esta opción hará que nuestro programa
+
   // La opción poWaitOnExit hará que nuestro programa
 
   // se detenga hasta que termine el programa lanzado
 
   // se detenga hasta que termine el programa lanzado
  //                                      vvvvvvvvvvvvvv
 
 
   UnProceso.Options := UnProceso.Options + [poWaitOnExit];
 
   UnProceso.Options := UnProceso.Options + [poWaitOnExit];
 
   
 
   
Line 46: Line 96:
 
   UnProceso.Execute;
 
   UnProceso.Execute;
 
   
 
   
   // Nuestro progama se detiene hasta que 'ppc386' finaliza.
+
   // Nuestro programa espera hasta que 'ppc386' finaliza.
 
   UnProceso.Free;   
 
   UnProceso.Free;   
  end.</delphi>
+
  end.</syntaxhighlight>
  
 
&nbsp;&nbsp;&nbsp;¡Eso es todo! Acabas de aprender cómo se ejecuta un programa externo desde dentro de tu propio programa.
 
&nbsp;&nbsp;&nbsp;¡Eso es todo! Acabas de aprender cómo se ejecuta un programa externo desde dentro de tu propio programa.
Line 57: Line 107:
 
&nbsp;&nbsp;&nbsp;Bien, vamos a ampliar un poco nuestro ejemplo mediante lo siguiente:
 
&nbsp;&nbsp;&nbsp;Bien, vamos a ampliar un poco nuestro ejemplo mediante lo siguiente:
  
<delphi> program launchprogram;
+
<syntaxhighlight lang=pascal>
 +
program lanzarprograma2;
 
   
 
   
 
  uses  
 
  uses  
Line 71: Line 122:
 
  // Aquí comienza a ejecutarse nuestro programa.
 
  // Aquí comienza a ejecutarse nuestro programa.
 
  begin
 
  begin
  // Ahora crearemos el objeto TProcess, y
 
  // lo asignamos a la variable UnProceso.
 
  UnProceso := TProcess.Create(nil);
 
 
   
 
   
 
   // Creamos el objeto TStringList.
 
   // Creamos el objeto TStringList.
 
   UnaLista := TStringList.Create;
 
   UnaLista := TStringList.Create;
 
   
 
   
   // Le decimos al nuevo UnProceso la orden que debe ejecutar.
+
   // Ahora creamos UnProceso.
   // Vamos a utilizar el compilador FreePascal.
+
  UnProceso := TProcess.Create(nil);
 +
 +
  // Asignamos a UnProceso la orden que debe ejecutar.
 +
   // Vamos a lanzazar el compilador de FreePascal
 +
  // (CommandLine está Obsoleto)
 
   UnProceso.CommandLine := 'ppc386 -h';
 
   UnProceso.CommandLine := 'ppc386 -h';
 
   
 
   
   // Definiremos una opción para cuando se ejecute
+
   // Definimos una opción de comportamiento de 'TProccess'
   // el programa. Esta opción hará que nuestro programa
+
   // La opción poWaitOnExit hará que nuestro programa
   // no continúe hasta que el programa que ejecutemos
+
   // se detenga hasta que termine el programa lanzado
   // termine. Ahora también le diremos que
+
   // con la opción poUsePipes    advertimos que vamos a utilizar tuberías de Entrada/Salida                                       
   // queremos leer la salida del archivo.
+
   // para que se capture la salida y así poder recuperar su contenido desde nuestro programa
 +
 
 
   UnProceso.Options := UnProceso.Options + [poWaitOnExit, poUsePipes];
 
   UnProceso.Options := UnProceso.Options + [poWaitOnExit, poUsePipes];
 +
 +
  // Lanzamos la ejecución de 'ppc386 -h'.
 +
  UnProceso.Execute;
 
   
 
   
   // Ahora que UnProceso conoce la orden, lo ejecutamos.
+
   // Nuestro programa se detiene hasta que 'ppc386' finaliza.
  UnProceso.Execute;
 
 
 
  // Esto ocurrirá cuando ppc386 termine de ejecutarse.
 
 
   
 
   
 
   // Ahora leemos la salida del programa que acabamos de ejecutar
 
   // Ahora leemos la salida del programa que acabamos de ejecutar
Line 101: Line 154:
 
   UnaLista.SaveToFile('salida.txt');
 
   UnaLista.SaveToFile('salida.txt');
 
   
 
   
   // Ahora que hemos guardado el archivo podemos liberar de la memoria
+
   // Ahora que hemos guardado el archivo podemos liberar la memoria
 
   // TStringList y TProcess.
 
   // TStringList y TProcess.
 
   UnaLista.Free;
 
   UnaLista.Free;
 
   UnProceso.Free;   
 
   UnProceso.Free;   
  end.</delphi>
+
  end.</syntaxhighlight>
  
 
=== Lectura de muchos datos de salida ===
 
=== Lectura de muchos datos de salida ===
En el ejemplo anterior esperábamos a que el programa terminara. En ese momento leíamos lo que el programa había escrito en su salida. Pero si el programa escribe muchos datos, la tubería se llena, será necesario leer, pero el programa que llama no leerá, hasta que el programa llamado haya terminado. Nos encontraremos en un punto muerto.
+
&nbsp;&nbsp;&nbsp;En el ejemplo anterior esperamos a que el programa termine. En ese momento leemos lo que el programa ha escrito en la salida. Pero si el programa escribe muchos datos, la tubería se llena y será necesario leerlos para vaciarla, pero el programa llamante no leerá hasta que el programa llamado haya terminado. Nos encontraremos ante un típico ''bloqueo de la muerte''.
  
El siguiente ejemplo, por consiguiente, no utiliza poWaitOnExit, sino que lee la salida mientras que el programa se está ejecutando. La salida se almacena en la memoria intermedia, que se puede utilizar más tarde para leerla en un TStringList.
+
&nbsp;&nbsp;&nbsp;El siguiente ejemplo, por tanto, no utiliza poWaitOnExit, sino que va leyendo la salida mientras el programa llamado se está ejecutando. La salida se almacena en una memoria intermedia, que se utilizará más tarde para leer en un TStringList, por ejemplo.
  
 +
<syntaxhighlight lang=pascal>
 
  program procoutlarge;
 
  program procoutlarge;
  {
+
  {   Copyright (c) 2004 by Marc Weustink
    Copyright (c) 2004 by Marc Weustink
 
 
   
 
   
 
     Este ejemplo se ha creado con la esperanza de que resulte útil,
 
     Este ejemplo se ha creado con la esperanza de que resulte útil,
Line 135: Line 188:
 
   
 
   
 
  begin
 
  begin
   // No podemos utilizar poWaitOnExit porquue no
+
   // No podemos utilizar poWaitOnExit porque no
 
   // conocemos el tamaño de la salida. En Linux el tamaño
 
   // conocemos el tamaño de la salida. En Linux el tamaño
 
   // de la salida de la tubería es de 2 kB. Si los datos de salida son más  
 
   // de la salida de la tubería es de 2 kB. Si los datos de salida son más  
Line 149: Line 202:
 
   P.CommandLine := 'ppc386 -va bogus.pp';
 
   P.CommandLine := 'ppc386 -va bogus.pp';
 
   P.Options := [poUsePipes];
 
   P.Options := [poUsePipes];
   WriteLn('-- executing --');
+
   WriteLn('-- ejecutando --');
 
   P.Execute;
 
   P.Execute;
 
   while P.Running do
 
   while P.Running do
Line 182: Line 235:
 
   if BytesRead > 0 then WriteLn;
 
   if BytesRead > 0 then WriteLn;
 
   M.SetSize(BytesRead);  
 
   M.SetSize(BytesRead);  
   WriteLn('-- executed --');
+
   WriteLn('-- ejecutado --');
 
    
 
    
 
   S := TStringList.Create;
 
   S := TStringList.Create;
 
   S.LoadFromStream(M);
 
   S.LoadFromStream(M);
   WriteLn('-- linecount = ', S.Count, ' --');
+
   WriteLn('-- total de líneas = ', S.Count, ' --');
 
   for n := 0 to S.Count - 1 do
 
   for n := 0 to S.Count - 1 do
 
   begin
 
   begin
 
     WriteLn('| ', S[n]);
 
     WriteLn('| ', S[n]);
 
   end;
 
   end;
   WriteLn('-- end --');
+
   WriteLn('-- fin --');
 
   S.Free;
 
   S.Free;
 
   P.Free;
 
   P.Free;
 
   M.Free;
 
   M.Free;
  end.
+
  end.</syntaxhighlight>
 +
 
 +
=== Uso de entrada y salida de un TProcess ===
 +
&nbsp;&nbsp;&nbsp;Véase el ejemplo processdemo en [https://lazarus-ccr.svn.sourceforge.net/svnroot/lazarus-ccr/examples/process Lazarus-CCR SVN]
  
 
=== Notas sobre la utilización de TProcess ===
 
=== Notas sobre la utilización de TProcess ===
Si está creando un programa de plataforma cruzada, puede cambiar la orden de acuerdo con el sistema operativo, mediante las directivas "{$IFDEF}s" y "{$ENDIF}s".
+
&nbsp;&nbsp;&nbsp;Si estas creando un programa que funciones en diversas plataformas, puedes cambiar el programa a ejecutar de acuerdo con el sistema operativo, mediante las directivas ''{$IFDEF}'' y ''{$ENDIF}''.
  
Ejemplo:
+
&nbsp;&nbsp;&nbsp;Ejemplo:
 +
<syntaxhighlight lang=pascal>
 
  {...}
 
  {...}
   AProcess:TProcess.Create(nil)
+
   unProceso:TProcess.Create(nil)
 
   {$IFDEF WIN32}
 
   {$IFDEF WIN32}
   AProcess.CommandLine := 'calc.exe'; //Windows Calc
+
   unProceso.CommandLine := 'calc.exe'; //calculadora de Windows
 
   {$ENDIF}
 
   {$ENDIF}
 
   {$IFDEF LINUX}
 
   {$IFDEF LINUX}
   AProcess.CommandLine := 'kcalc'; //KDE Calc
+
   unProceso.CommandLine := 'kcalc'; //calculadora de KDE o bien xcalc si no tienes KDE
 
   {$ENDIF}
 
   {$ENDIF}
   AProcess.Execute; //in alternative, you can use AProcess.Active:=True
+
   unProceso.Execute; //también puedes utilizar unProceso.Active:=True
  {...}
+
  {...}</syntaxhighlight>
 +
 
 +
En cuanto a TProcess tomando como referencia las propiedades del componente visual tiene las siguientes opciones (po=process options):
 +
 
 +
* poDebugOnlyThisProcess.
 +
* poDebugProcess.
 +
* poDefaultErrorMode
 +
* poNewConsole.
 +
* poNewProcessGroup.
 +
* poNoConsole.
 +
* poRunSuspended.
 +
* poStderrToOutPut.
 +
* poUsePipes: utilizado para interconectar procesos.
 +
* poWaitOnExit: esperar a que finalice el programa.
 +
 
 +
[[image:TProcess_Options.PNG|center|300px]]
 +
 
 +
A falta de explicar que hace cada opción.
 +
 
 +
[http://lazarus-ccr.sourceforge.net/docs/fcl/process/tprocess.html Referencia TProcess].
  
=== Ejemplo de "talking" con el proceso aspell ===
+
=== Ejemplo de ''conversación'' con el proceso aspell ===
  
Dentro del código fuente [http://pasdoc.sourceforge.net/ pasdoc] puede encontrar dos unidades que realizan corrección ortográfica mediante "talking" con ejecución del proceso aspell a través de tuberías:
+
&nbsp;&nbsp;&nbsp;Dentro del código fuente [http://pasdoc.sourceforge.net/ pasdoc] puedes encontrar dos unidades que realizan corrección ortográfica ''conversando'' con un proceso aspell a través de tuberías:
  
 
* [http://cvs.sourceforge.net/viewcvs.py/*checkout*/pasdoc/pasdoc/source/component/PasDoc_ProcessLineTalk.pas PasDoc_ProcessLineTalk.pas unit] ejecuta la clase TProcessLineTalk, descendiente de TProcess, que se puede utilizar fácilmente para comunicar con cualquier proceso línea a línea.
 
* [http://cvs.sourceforge.net/viewcvs.py/*checkout*/pasdoc/pasdoc/source/component/PasDoc_ProcessLineTalk.pas PasDoc_ProcessLineTalk.pas unit] ejecuta la clase TProcessLineTalk, descendiente de TProcess, que se puede utilizar fácilmente para comunicar con cualquier proceso línea a línea.
Line 220: Line 296:
 
* [http://cvs.sourceforge.net/viewcvs.py/*checkout*/pasdoc/pasdoc/source/component/PasDoc_Aspell.pas PasDoc_Aspell.pas units] ejecuta la clase TAspellProcess, que realiza corrección ortográfica utilizando la instancia fundamental TProcessLineTalk para ejecutar aspell y comunicarse con el proceso aspell que se está ejecutando.
 
* [http://cvs.sourceforge.net/viewcvs.py/*checkout*/pasdoc/pasdoc/source/component/PasDoc_Aspell.pas PasDoc_Aspell.pas units] ejecuta la clase TAspellProcess, que realiza corrección ortográfica utilizando la instancia fundamental TProcessLineTalk para ejecutar aspell y comunicarse con el proceso aspell que se está ejecutando.
  
Ambas unidades son bastante independientes del resto de fuentes de pasdoc, así que pueden servir como ejemplos del mundo real de la utilización de TProcess para ejecutar y comunicarse a través de tuberías con otros programas.
+
&nbsp;&nbsp;&nbsp;Ambas unidades son bastante independientes del resto de fuentes de pasdoc, así que pueden servir como ejemplos reales de la utilización de TProcess para ejecutar y comunicarse a través de tuberías con otros programas.
 +
 
 +
=== Reemplazar operadores de ''Shell'' como  "|",  "<" y  ">" ===
 +
 
 +
&nbsp;&nbsp;&nbsp;A veces queremos ejecutar una orden compleja que envía (entuba) sus datos a otra orden o a un archivo. Algo como
 +
 
 +
  ShellExecute('primeraorden.exe | segundaorden.exe');
 +
o
 +
  ShellExecute('dir > salida.txt');
 +
 
 +
&nbsp;&nbsp;&nbsp; Esto no funciona con TProcess. p.e.:
 +
  // esto no funciona
 +
  Process.CommandLine := 'primeraorden.exe | segundaorden.exe';
 +
  Process.Execute;
 +
 
 +
==== ¿Por qué utilizar operadores especiales para redirigir la salida no funciona? ====
 +
&nbsp;&nbsp;&nbsp;TProcess es justamente, eso, un proceso, no es un entorno de ''shell''. No es dos procesos, únicamente es un proceso. Es posible, no obstante, redirigir la salida de la manera que queremos. Ver [[Executing_External_Programs/es#Cómo_redirigir_la_salida_con_TProcess|más adelante, la siguiente sección]].
 +
 
 +
=== Cómo redirigir la salida con TProcess ===
 +
 
 +
&nbsp;&nbsp;&nbsp;Podemos redirigir la salidad de un comando a otro comando utilizando una instancia de TProcess para cada commando.
 +
 
 +
&nbsp;&nbsp;&nbsp;Este es un ejemplo que explica cómo redirigir la salida de un proceso a otro. Para redirigir la salida de un proceso a un archivo o flujo ver el ejemplo [[Executing_External_Programs/es#Lectura de muchos datos de salida | Lectura de muchos datos de salida ]]
 +
<syntaxhighlight lang=pascal>
 +
program Proyecto1;
 +
 
 +
  uses
 +
    Classes, sysutils, process;
 +
 
 +
  var
 +
    PrimerProceso,
 +
    SegundoProceso: TProcess;
 +
    Buffer: array[0..127] of char;
 +
    ContadorLectura: Integer;
 +
    TamanyoLectura: Integer;
 +
  begin
 +
    PrimerProceso  := TProcess.Create(nil);
 +
    SegundoProceso := TProcess.Create(nil);
 +
 
 +
    PrimerProceso.Options  := [poUsePipes];
 +
    SegundoProceso.Options := [poUsePipes,poStderrToOutPut];
 +
 
 +
    PrimerProceso.CommandLine  := 'pwd';
 +
    SegundoProceso.CommandLine := 'grep '+ DirectorySeparator+ ' -';
 +
    // esto sería lo mismo que "pwd | grep / -"
 +
 
 +
    PrimerProceso.Execute;
 +
    SegundoProceso.Execute;
 +
 
 +
    while PrimerProceso.Running or (PrimerProceso.Output.NumBytesAvailable > 0) do
 +
    begin
 +
      if PrimerProceso.Output.NumBytesAvailable > 0 then
 +
      begin
 +
      // asegurate de que no lees más datos que los asignados
 +
      // en el buffer
 +
        TamanyoLectura := PrimerProceso.Output.NumBytesAvailable;
 +
        if TamanyoLectura > SizeOf(Buffer) then
 +
          TamanyoLectura := SizeOf(Buffer);
 +
        // ahora leemos la salida en el buffer
 +
        ContadorLectura := PrimerProceso.Output.Read(Buffer[0], TamanyoLectura);
 +
        // y escribimos el buffer en el segundo proceso
 +
        SegundoProceso.Input.Write(Buffer[0], ContadorLectura);
 +
 
 +
        // Si SegundoProceso escribe muchos datos en la salida a continuación,
 +
        // debes leer los datos aquí para evitar un punto muerto
 +
        // ver el ejemplo anterior "Lectura de muchos datos de salida"
 +
      end;
 +
    end;
 +
    // Cerrar la entrad de SegundoProceso
 +
    // y finaliza el procesamiento de datos
 +
    SegundoProceso.CloseInput;
 +
 
 +
    // y esperar a que finalice
 +
    // ten cuidado con que comando se ejecuta porque no puede terminar cuando
 +
    // la entrada está cerrada y la siguiente línea será un bucle sin fin
 +
    while SegundoProceso.Running do
 +
      Sleep(1);
 +
    // ¡eso es todo! el resto del programa es sólo un ejemplo
 +
    // es poco "útil"
 +
 
 +
    // vamos a  reutilizar el buffer para la salida del SegundoProceso
 +
    // a la salida estándar de este programa
 +
    WriteLn('Comienza la salida de Grep:');
 +
    TamanyoLectura := SegundoProceso.Output.NumBytesAvailable;
 +
    if TamanyoLectura > SizeOf(Buffer) then
 +
      TamanyoLectura := SizeOf(Buffer);
 +
    if TamanyoLectura > 0 then
 +
    begin
 +
      ContadorLectura := SegundoProceso.Output.Read(Buffer, TamanyoLectura);
 +
      WriteLn(Copy(Buffer,0, ContadorLectura));
 +
    end
 +
    else
 +
      WriteLn('grep NO encontró lo que se buscaba. ', SegundoProceso.ExitStatus);
 +
    WriteLn('Termina la salida de Grep:');
 +
 
 +
    // Liberamos los objetos TProcess
 +
    PrimerProceso.Free;
 +
    SegundoProceso.Free;
 +
  end.</syntaxhighlight>
 +
 
 +
&nbsp;&nbsp;&nbsp;Eso es todo. Ahora podemos redirigir la salida de un programa a otro.
 +
==== Notas ====
 +
&nbsp;&nbsp;&nbsp;Este ejemplo puede parecer exagerado, ya que es posible ejecutar "complicados" comandos shell con TProcess como:
 +
  Process.Commandline := 'sh -c "pwd | grep / -"';
 +
&nbsp;&nbsp;&nbsp;Pero el ejemplo es más multiplataforma, ya que no necesita ninguna modificación para ejecutarse en Windows o Linux, etc. ''sh'' puede o no existir en su plataforma y, en general sólo disponible en plataformas *nix. También tenemos más flexibilidad en nuestro ejemplo ya que se puede leer y escribir desde/a la entrada, salida y salida de error estándar de cada proceso individual, lo que podría ser muy ventajoso para tu proyecto.
 +
 
 +
===Redirigir entrada y salida bajo ''root''===
 +
 
 +
&nbsp;&nbsp;&nbsp;Un problema común en Unix (macOS) y Linux surge cuando se desea ejecutar algún programa en la cuenta de ''root'' (o, en otra cuenta de usuario). Un ejemplo sería ejecutar el comando ping''''.
 +
 
 +
&nbsp;&nbsp;&nbsp;Si puedes usar sudo para esto, se puede adaptar el ejemplo siguiente, una adaptación de uno publicado por andyman en el foro ([http://lazarus.freepascal.org/index.php/topic,14479.0.html]). Este ejemplo ejecuta la orden <code>ls</code> en el <code>/root</code>, pero por supuesto puede ser adaptada.
 +
 
 +
&nbsp;&nbsp;&nbsp;"Una forma mejor'' de hacerlo es usar el paquete PolicyKit, que debería estar disponible en todos los Linux recientes. [http://lazarus.freepascal.org/index.php/topic,14479.0.html ver el hilo del foro para más detalles.]
 +
 
 +
&nbsp;&nbsp;&nbsp;Gran parte de este código es similar al ejemplo anterior, pero también se muestra cómo redirigir los flujos stdout y stderr del proceso llamado a los flujos stdout y stderr de nuestro propio código.
 +
 
 +
<syntaxhighlight lang=pascal>
 +
program rootls;
 +
 
 +
{ Demonstrates using TProcess, redirecting stdout/stderr to our stdout/stderr,
 +
calling sudo on Linux/macOS, 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.</syntaxhighlight>
 +
 
 +
&nbsp;&nbsp;&nbsp;Otras cuestiones:
 +
 
 +
&nbsp;&nbsp;&nbsp;Sería, sin duda, aconsejable ver si realmente ''sudo'' pide una contraseña. Esto se puede comprobar consistetemente estableciendo la variable de entorno ''SUDO_PROMPT'' a algo que tener en cuenta mientras la lectura de la salida estándar de '''''TProcess''''' para evitar el problema de que el sistema es diferente para diferentes idiomas. Configurar una variable de entorno hace que los valores predeterminados (heredados de nuestro proceso) se borren, así que tenemos que copiar el entorno de nuestro programa, si es necesario.
 +
 
 +
===Parámetros que contienen espacios (Reemplazar comillas del interprete)===
 +
 
 +
&nbsp;&nbsp;&nbsp;En el interprete de Linux, es posible escribir los argumentos entrecomillados así:
 +
 
 +
  gdb --batch --eval-command="info symbol 0x0000DDDD" miprograma
 +
 
 +
&nbsp;&nbsp;&nbsp;Y GDB recibirá 3 argumentos (además del primer argumento que es la ruta completa al ejecutable):
 +
 
 +
  #--batch
 +
  #--eval-command=info symbol 0x0000DDDD
 +
  #la ruta completa a miprograma
 +
 
 +
&nbsp;&nbsp;&nbsp;'''TProcess''' además pasa los parámetros que contengan espacios, pero utiliza un estilo diferente de entrecomillado. En lugar de sólo entrecomillar parte del parámetro, entrecomilla todo él. Tal que así:
 +
 
 +
  TProcess.CommandLine := '/usr/bin/gdb --batch "--eval-command=info symbol 0x0000DDDD" /midirectorio/yo/miprograma';
 +
 
 +
&nbsp;&nbsp;&nbsp;Y también recuerda pasar sólo rutas completas.
 +
 
 +
&nbsp;&nbsp;&nbsp;Véase también la discusión al respecto: http://bugs.freepascal.org/view.php?id=14446
 +
 
 +
==Alternativas==
 +
 
 +
&nbsp;&nbsp;&nbsp;A veces, no es necesario llamar explícitamente a un programa externo para obtener la funcionalidad que necesitas. A continuación se presentan algunos ejemplos
 +
 
 +
===Abrir una página web en el navegador web predeterminado===
 +
 
 +
&nbsp;&nbsp;&nbsp;Algo así sería más fácil:
 +
 
 +
<syntaxhighlight lang=pascal>
 +
uses LCLIntf;
 +
...
 +
OpenURL(URL);</syntaxhighlight>
 +
 
 +
&nbsp;&nbsp;&nbsp;Sin embargo, puedes utilizar algo como esto:
 +
 
 +
<syntaxhighlight lang=pascal>
 +
uses Process, LCLIntf;
 +
 
 +
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); // elimina "", la nueva versión de TProcess.Parameters los hace por sí mismo
 +
    Parameters.Add(Params);
 +
    Options := [poNoConsole];
 +
    Execute;
 +
  finally
 +
    Free;
 +
  end;
 +
end;</syntaxhighlight>
 +
 
 +
==Ver también==
 +
 
 +
* [[TXMLPropStorage/es|TXMLPropStorage]]
 +
* [[Webbrowser]]

Latest revision as of 20:07, 26 August 2020

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)

Introducción

   Hay varias formas de ejecutar programas externos, pero nos centraremos en una, TProcess.

Si en Delphi utilizabas siempre ShellExecute y/o WinExec, puedes empezar a usar TProcess como alternativa en FPC/Lazarus (es válido también si estás ejecutando Lazarus en Linux, porque TProcess es multiplataforma).

   Nota: FPC/Lazarus puede utilizar ShellExecute y/o WinExec, pero sólo en Win32. Si tu programa está enfocado a varias plataformas, utiliza TProcess, ¡es mejor!.


Aquí tenemos las diferentes maneras disponibles sobre como ejecutar un programa/proceso/mandato externo en RTL, FCL y LCL.

Metodo Librería Plataformas Línea simple Características
ExecuteProcess RTL Plataforma Cruzada Si Muy limitado, synchronous
ShellExecute WinAPI MS Windows only Yes Muchos. Permite iniciar programas con permisos de elevación/admin.
fpsystem, fpexecve Unix Unix solamente
TProcess FCL Plataforma cruzada No Completas
RunCommand FCL Plataforma cruzada, Requiere FPC 2.6.2+ Si Cubre el uso común de TProcess
OpenDocument LCL Plataforma cruzada si Solo para abrir documentos. El documento debe abrirse con la aplicación asociada al tipo de documento.

TProcess

   Puedes utilizar TProcess para ejecutar programas externos. Algunas de sus ventajas son:

  • Independiente de la plataforma
  • Capaz de leer desde la entrada estándar (stdout) y de escribir en la salida estándar (stdin).

   Nota: TProcess no es un terminal de shell! No puedes ejecutar directamente secuencias de comandos o reorientar la salida usando operadores como "|", ">", "<", "&", etc. Es posible obtener los mismos resultados con TProcess utilizando Pascal, mira algunos ejemplos más abajo..

   Importante: Debes especificar la ruta completa al ejecutable. Por ejemplo "/bin/cp " en lugar de "cp". Si el programa está en la ruta estándar, puedes utilizar la función FindDefaultExecutablePath de la unidad FileUtil de la LCL.

Un ejemplo sencillo

 // Este es un programa de demostración que enseña como lanzar
 // un programa externo.
 program lanzarprograma;
 
 uses 
   Classes, SysUtils, Process;
 
 // Definición de "UnProceso" como una variable 
 // de tipo "TProcess"
 var 
   UnProceso: TProcess;

 begin
   // Ahora creamos UnProceso.
   UnProceso := TProcess.Create(nil);
 
   // Asignamos a UnProceso la orden que debe ejecutar.
   // Vamos a lanzar el compilador de FreePascal 
   UnProceso.CommandLine := 'ppc386 -h';
 
   // Definimos una opción de comportamiento de 'TProccess'
   // La opción poWaitOnExit hará que nuestro programa
   // se detenga hasta que termine el programa lanzado
   UnProceso.Options := UnProceso.Options + [poWaitOnExit];
 
   // Lanzamos la ejecución de 'ppc386 -h'.
   UnProceso.Execute;
 
   // Nuestro programa espera hasta que 'ppc386' finaliza.
   UnProceso.Free;   
 end.

   ¡Eso es todo! Acabas de aprender cómo se ejecuta un programa externo desde dentro de tu propio programa.

Un ejemplo mejorado

   De acuerdo, pero ¿cómo leo la salida que produce un programa que he ejecutado?

   Bien, vamos a ampliar un poco nuestro ejemplo mediante lo siguiente:

 program lanzarprograma2;
 
 uses 
   Classes, SysUtils, Process;
 
 // Definición de "UnProceso" como una variable de tipo "TProcess"
 // También añadimos un TStringList para almacenar los datos 
 // leídos desde la salida del programa.
 var 
   UnProceso: TProcess;
   UnaLista: TStringList;
 
 // Aquí comienza a ejecutarse nuestro programa.
 begin
 
   // Creamos el objeto TStringList.
   UnaLista := TStringList.Create;
 
   // Ahora creamos UnProceso.
   UnProceso := TProcess.Create(nil);
 
   // Asignamos a UnProceso la orden que debe ejecutar.
   // Vamos a lanzazar el compilador de FreePascal
   // (CommandLine está Obsoleto)
   UnProceso.CommandLine := 'ppc386 -h';
 
   // Definimos una opción de comportamiento de 'TProccess'
   // La opción poWaitOnExit hará que nuestro programa
   // se detenga hasta que termine el programa lanzado
   // con la opción poUsePipes    advertimos que vamos a utilizar tuberías de Entrada/Salida                                        
   // para que se capture la salida y así poder recuperar su contenido desde nuestro programa

   UnProceso.Options := UnProceso.Options + [poWaitOnExit, poUsePipes];

   // Lanzamos la ejecución de 'ppc386 -h'.
   UnProceso.Execute;
 
   // Nuestro programa se detiene hasta que 'ppc386' finaliza.
 
   // Ahora leemos la salida del programa que acabamos de ejecutar
   // dentro de TStringList.
   UnaLista.LoadFromStream(UnProceso.Output);
   
   // Guardamos la salida en un archivo.
   UnaLista.SaveToFile('salida.txt');
 
   // Ahora que hemos guardado el archivo podemos liberar la memoria
   // TStringList y TProcess.
   UnaLista.Free;
   UnProceso.Free;   
 end.

Lectura de muchos datos de salida

   En el ejemplo anterior esperamos a que el programa termine. En ese momento leemos lo que el programa ha escrito en la salida. Pero si el programa escribe muchos datos, la tubería se llena y será necesario leerlos para vaciarla, pero el programa llamante no leerá hasta que el programa llamado haya terminado. Nos encontraremos ante un típico bloqueo de la muerte.

   El siguiente ejemplo, por tanto, no utiliza poWaitOnExit, sino que va leyendo la salida mientras el programa llamado se está ejecutando. La salida se almacena en una memoria intermedia, que se utilizará más tarde para leer en un TStringList, por ejemplo.

 program procoutlarge;
 {   Copyright (c) 2004 by Marc Weustink
 
     Este ejemplo se ha creado con la esperanza de que resulte útil,
     pero SIN NINGUNA GARANTÍA; ni tan siquiera la garantía implícita de
     COMERCIALIZACIÓN o ADECUACIÓN A UN OBJETIVO PARTICULAR.
 }
 
 uses
   Classes, Process, SysUtils;
 
 const
   READ_BYTES = 2048;
   
 var
   S: TStringList;
   M: TMemoryStream;
   P: TProcess;
   n: LongInt;
   BytesRead: LongInt;
 
 begin
   // No podemos utilizar poWaitOnExit porque no
   // conocemos el tamaño de la salida. En Linux el tamaño
   // de la salida de la tubería es de 2 kB. Si los datos de salida son más 
   // necesitamos leerlos. Esto no es posible, ya que estamos
   // esperando. Así que aquí nos encontramos en un punto muerto.
   //
   // Se utiliza un Memorystream temporal para almacenar la salida
   
   M := TMemoryStream.Create;
   BytesRead := 0;
 
   P := TProcess.Create(nil);
   P.CommandLine := 'ppc386 -va bogus.pp';
   P.Options := [poUsePipes];
   WriteLn('-- ejecutando --');
   P.Execute;
   while P.Running do
   begin          
     // nos aseguramos de tener espacio
     M.SetSize(BytesRead + READ_BYTES);
     
     // intentamos leerlo
     n := P.Output.Read((M.Memory + BytesRead)^, READ_BYTES);
     if n > 0 
     then begin
       Inc(BytesRead, n);
       Write('.')
     end
     else begin     
       // sin datos, esperamos 100 milisegundos
       Sleep(100); 
     end;
   end;
   // leemos la última parte
   repeat
     // nos aseguramos de tener espacio
     M.SetSize(BytesRead + READ_BYTES);
     // intentamos leerlo
     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('-- ejecutado --');
   
   S := TStringList.Create;
   S.LoadFromStream(M);
   WriteLn('-- total de líneas = ', S.Count, ' --');
   for n := 0 to S.Count - 1 do
   begin
     WriteLn('| ', S[n]);
   end;
   WriteLn('-- fin --');
   S.Free;
   P.Free;
   M.Free;
 end.

Uso de entrada y salida de un TProcess

   Véase el ejemplo processdemo en Lazarus-CCR SVN

Notas sobre la utilización de TProcess

   Si estas creando un programa que funciones en diversas plataformas, puedes cambiar el programa a ejecutar de acuerdo con el sistema operativo, mediante las directivas {$IFDEF} y {$ENDIF}.

   Ejemplo:

 {...}
   unProceso:TProcess.Create(nil)
   {$IFDEF WIN32}
   unProceso.CommandLine := 'calc.exe'; //calculadora de Windows
   {$ENDIF}
   {$IFDEF LINUX}
   unProceso.CommandLine := 'kcalc'; //calculadora de KDE o bien xcalc si no tienes KDE
   {$ENDIF}
   unProceso.Execute; //también puedes utilizar unProceso.Active:=True
 {...}

En cuanto a TProcess tomando como referencia las propiedades del componente visual tiene las siguientes opciones (po=process options):

  • poDebugOnlyThisProcess.
  • poDebugProcess.
  • poDefaultErrorMode
  • poNewConsole.
  • poNewProcessGroup.
  • poNoConsole.
  • poRunSuspended.
  • poStderrToOutPut.
  • poUsePipes: utilizado para interconectar procesos.
  • poWaitOnExit: esperar a que finalice el programa.
TProcess Options.PNG

A falta de explicar que hace cada opción.

Referencia TProcess.

Ejemplo de conversación con el proceso aspell

   Dentro del código fuente pasdoc puedes encontrar dos unidades que realizan corrección ortográfica conversando con un proceso aspell a través de tuberías:

  • PasDoc_ProcessLineTalk.pas unit ejecuta la clase TProcessLineTalk, descendiente de TProcess, que se puede utilizar fácilmente para comunicar con cualquier proceso línea a línea.
  • PasDoc_Aspell.pas units ejecuta la clase TAspellProcess, que realiza corrección ortográfica utilizando la instancia fundamental TProcessLineTalk para ejecutar aspell y comunicarse con el proceso aspell que se está ejecutando.

   Ambas unidades son bastante independientes del resto de fuentes de pasdoc, así que pueden servir como ejemplos reales de la utilización de TProcess para ejecutar y comunicarse a través de tuberías con otros programas.

Reemplazar operadores de Shell como "|", "<" y ">"

   A veces queremos ejecutar una orden compleja que envía (entuba) sus datos a otra orden o a un archivo. Algo como

 ShellExecute('primeraorden.exe | segundaorden.exe');

o

 ShellExecute('dir > salida.txt');

    Esto no funciona con TProcess. p.e.:

 // esto no funciona
 Process.CommandLine := 'primeraorden.exe | segundaorden.exe'; 
 Process.Execute;

¿Por qué utilizar operadores especiales para redirigir la salida no funciona?

   TProcess es justamente, eso, un proceso, no es un entorno de shell. No es dos procesos, únicamente es un proceso. Es posible, no obstante, redirigir la salida de la manera que queremos. Ver más adelante, la siguiente sección.

Cómo redirigir la salida con TProcess

   Podemos redirigir la salidad de un comando a otro comando utilizando una instancia de TProcess para cada commando.

   Este es un ejemplo que explica cómo redirigir la salida de un proceso a otro. Para redirigir la salida de un proceso a un archivo o flujo ver el ejemplo Lectura de muchos datos de salida

 program Proyecto1;

  uses
    Classes, sysutils, process;

  var
    PrimerProceso,
    SegundoProceso: TProcess;
    Buffer: array[0..127] of char;
    ContadorLectura: Integer;
    TamanyoLectura: Integer;
  begin
    PrimerProceso  := TProcess.Create(nil);
    SegundoProceso := TProcess.Create(nil);

    PrimerProceso.Options  := [poUsePipes];
    SegundoProceso.Options := [poUsePipes,poStderrToOutPut];

    PrimerProceso.CommandLine  := 'pwd';
    SegundoProceso.CommandLine := 'grep '+ DirectorySeparator+ ' -';
    // esto sería lo mismo que "pwd | grep / -"

    PrimerProceso.Execute;
    SegundoProceso.Execute;

    while PrimerProceso.Running or (PrimerProceso.Output.NumBytesAvailable > 0) do
    begin
      if PrimerProceso.Output.NumBytesAvailable > 0 then
      begin
      // asegurate de que no lees más datos que los asignados
      // en el buffer
        TamanyoLectura := PrimerProceso.Output.NumBytesAvailable;
        if TamanyoLectura > SizeOf(Buffer) then
          TamanyoLectura := SizeOf(Buffer);
        // ahora leemos la salida en el buffer
        ContadorLectura := PrimerProceso.Output.Read(Buffer[0], TamanyoLectura);
        // y escribimos el buffer en el segundo proceso
        SegundoProceso.Input.Write(Buffer[0], ContadorLectura);

        // Si SegundoProceso escribe muchos datos en la salida a continuación,
        // debes leer los datos aquí para evitar un punto muerto
        // ver el ejemplo anterior "Lectura de muchos datos de salida"
      end;
    end;
    // Cerrar la entrad de SegundoProceso
    // y finaliza el procesamiento de datos
    SegundoProceso.CloseInput;

     // y esperar a que finalice
     // ten cuidado con que comando se ejecuta porque no puede terminar cuando
     // la entrada está cerrada y la siguiente línea será un bucle sin fin
    while SegundoProceso.Running do
      Sleep(1);
     // ¡eso es todo! el resto del programa es sólo un ejemplo
     // es poco "útil"

     // vamos a  reutilizar el buffer para la salida del SegundoProceso
     // a la salida estándar de este programa
    WriteLn('Comienza la salida de Grep:');
    TamanyoLectura := SegundoProceso.Output.NumBytesAvailable;
    if TamanyoLectura > SizeOf(Buffer) then
      TamanyoLectura := SizeOf(Buffer);
    if TamanyoLectura > 0 then
    begin
      ContadorLectura := SegundoProceso.Output.Read(Buffer, TamanyoLectura);
      WriteLn(Copy(Buffer,0, ContadorLectura));
    end
    else
      WriteLn('grep NO encontró lo que se buscaba. ', SegundoProceso.ExitStatus);
    WriteLn('Termina la salida de Grep:');

    // Liberamos los objetos TProcess
    PrimerProceso.Free;
    SegundoProceso.Free;
  end.

   Eso es todo. Ahora podemos redirigir la salida de un programa a otro.

Notas

   Este ejemplo puede parecer exagerado, ya que es posible ejecutar "complicados" comandos shell con TProcess como:

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

   Pero el ejemplo es más multiplataforma, ya que no necesita ninguna modificación para ejecutarse en Windows o Linux, etc. sh puede o no existir en su plataforma y, en general sólo disponible en plataformas *nix. También tenemos más flexibilidad en nuestro ejemplo ya que se puede leer y escribir desde/a la entrada, salida y salida de error estándar de cada proceso individual, lo que podría ser muy ventajoso para tu proyecto.

Redirigir entrada y salida bajo root

   Un problema común en Unix (macOS) y Linux surge cuando se desea ejecutar algún programa en la cuenta de root (o, en otra cuenta de usuario). Un ejemplo sería ejecutar el comando ping'.

   Si puedes usar sudo para esto, se puede adaptar el ejemplo siguiente, una adaptación de uno publicado por andyman en el foro ([1]). Este ejemplo ejecuta la orden ls en el /root, pero por supuesto puede ser adaptada.

   "Una forma mejor de hacerlo es usar el paquete PolicyKit, que debería estar disponible en todos los Linux recientes. ver el hilo del foro para más detalles.

   Gran parte de este código es similar al ejemplo anterior, pero también se muestra cómo redirigir los flujos stdout y stderr del proceso llamado a los flujos stdout y stderr de nuestro propio código.

 program rootls;

 { Demonstrates using TProcess, redirecting stdout/stderr to our stdout/stderr,
 calling sudo on Linux/macOS, 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.

   Otras cuestiones:

   Sería, sin duda, aconsejable ver si realmente sudo pide una contraseña. Esto se puede comprobar consistetemente estableciendo la variable de entorno SUDO_PROMPT a algo que tener en cuenta mientras la lectura de la salida estándar de TProcess para evitar el problema de que el sistema es diferente para diferentes idiomas. Configurar una variable de entorno hace que los valores predeterminados (heredados de nuestro proceso) se borren, así que tenemos que copiar el entorno de nuestro programa, si es necesario.

Parámetros que contienen espacios (Reemplazar comillas del interprete)

   En el interprete de Linux, es posible escribir los argumentos entrecomillados así:

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

   Y GDB recibirá 3 argumentos (además del primer argumento que es la ruta completa al ejecutable):

 #--batch
 #--eval-command=info symbol 0x0000DDDD
 #la ruta completa a miprograma

   TProcess además pasa los parámetros que contengan espacios, pero utiliza un estilo diferente de entrecomillado. En lugar de sólo entrecomillar parte del parámetro, entrecomilla todo él. Tal que así:

 TProcess.CommandLine := '/usr/bin/gdb --batch "--eval-command=info symbol 0x0000DDDD" /midirectorio/yo/miprograma';

   Y también recuerda pasar sólo rutas completas.

   Véase también la discusión al respecto: http://bugs.freepascal.org/view.php?id=14446

Alternativas

   A veces, no es necesario llamar explícitamente a un programa externo para obtener la funcionalidad que necesitas. A continuación se presentan algunos ejemplos

Abrir una página web en el navegador web predeterminado

   Algo así sería más fácil:

 uses LCLIntf;
 ...
 OpenURL(URL);

   Sin embargo, puedes utilizar algo como esto:

 uses Process, LCLIntf;

 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); // elimina "", la nueva versión de TProcess.Parameters los hace por sí mismo
    Parameters.Add(Params);
    Options := [poNoConsole];
    Execute;
  finally
    Free;
  end;
 end;

Ver también