PDA

Ver la Versión Completa : Capturar salida de comando linux


pkbza
11-01-2006, 15:58:16
Hola foro del clubdelphi
Tengo una pequeña aplicacion en kylix que necesita ejecutar comandos en linux, bueno eso lo hago sin mayor problema con execl, el problema es que ahora tambien necesito rescatar o capturar la salida de un comando (que es solo una linea, siempre) y guardarlo en alguna variable de mi programa, no he encontrado forma de hacerlo, aca tampoco he encontrado algun hilo sobre el tema, si alguien ha hecho eso alguna vez le pediria que me alludase porfavor, gracias de antemano.

mg1821
12-01-2006, 04:39:49
tienes que colocar en el uses el libC
creo que navegando en el foro puedes encontrar al respecto. borland publico al respecto hace tiempo, tengo el archivo pero increiblemente no lo puedo adjuntar ya que me bota error de archivo. (uhm.....sera que no puedo adjuntar desde linux?.......no creo.....):cool:

jachguate
12-01-2006, 04:53:34
Tomá en cuenta que adjuntar archivos tiene un límite (bastante restrictivo) de tamaño.

No será que es un archivo mas grande de lo aceptado?

pkbza
12-01-2006, 13:22:09
Gracias mg1821, pero en el foro no he encontrado nada sobre eso... efectivamente utilizo libC para ejecutar los comandos, pero no se como rescatar la salida standard de estos, si quieres me puedes enviar el archivo que no pudiste adjuntar a mi casilla de correo, esta es pbarrera@lintex.cl, gracias nuevamente, saludos.

mg1821
12-01-2006, 21:49:42
aqui esta el archivo completo, prefiero colocarlo asi para que este a la vista de todos. disculpas si esto no estaba permitido (el archivo era solo de 17k:confused: )

Starting an external application with Kylix
Here are three methods of invoking external applications. By Matthias Thoma.

Method 1: LibC.System function call
function system(Command: PChar): Integer;
The Libc.System function call is the most convenient way to start an external application. It simply executes the given argument command as shell command.
system is a blocking function, which means it will not return until the child process has terminated. The result value is -1 or 127 if an error occurred. The value 127 has the special meaning that execve (used within the system function - a more detailed description follows) failed.
On some shells you can add an ampersand (the "&" char) to the argument command. This will force the system function call to return immediately:

procedure PlayWave;
begin
if Libc.system('playwav ~/test.wav') = -1 then
begin
ShowMessage('Can''t play wav file');
end;
end;
Example 1.1: Play a wav file using system

You can determine if a shell is available by using nil as argument. If the result value is not zero, system was unable to invoke a shell.

procedure ShellAvail;
begin
if Libc.system(nil) <> 0 then
begin
ShowMessage('No shell available.');
end;
end;
Example 1.2: Determine if a shell is available

Method 2: popen
This method is extremely useful when you need to get the standard output of the external application. The popen function is declared as

function popen(const Command: PChar; Modes: PChar): PIOFile; cdecl;

It invokes the shell, runs the command, and creates a pipe. This demo simply displays the contents of the current directory within a memo.

var
Output: PIOFile;
line: PChar;
txt: string;
str: string;
StrLst: TStringList;
rb: integer;
const
BufferSize: Integer = 1000;
begin
SetLength(txt,0);
Output := popen('ls -l','r');
StrLst := TStringList.Create;
GetMem(Line,BufferSize);
StrLst.Add('Hallo');
if Assigned(output) then
begin
while FEOF(Output)=0 do
begin
rb := libc.fread(line,1,BufferSize,output);
SetLength(Txt,length(txt)+rb);
MemCpy(@txt[length(txt)-(rb-1)],line,rb);
while pos(#10,txt) > 0 do
begin
str := copy(txt,1,pos(#10,txt)-1);
StrLst.Add(str);
txt := copy(txt,pos(#10,txt)+1,length(txt));
end;
end;
end;
Memo1.Lines.AddStrings(StrLst);
StrLst.Free;
libc.pclose(output);
wait(nil);
FreeMem(Line,BufferSize);
end;

Method 3: Fork and Exec
Fork and Exec is how hardcore Linux and Unix programmers would create a child process. In fact, the previous functions are internally implemented using fork and exec.
function fork: __pid_t; cdecl;
The fork function makes a copy the parent process and therefore creates a new subprocess. Within the parent process the result value of fork is the Process ID (PID) of the subprocess or an error code. In the newly created subprocess the result value is zero.
Note: The function fork and the Kylix debugger do not seem to be 100% compatible currently. If you encounter problems and you are running the application within the IDE it is recommended that you disable the debugger.

{$APPTYPE CONSOLE}
uses
Libc;
procedure Forking;
var
pid: PID_T;
begin
pid := fork;
if pid = 0 then
begin
writeln('Hello. I am your new sub-process.');
writeln('Bye.');
halt;
end;
writeln('Hello. I am the parent process. I am going to wait some time...');
__sleep(3);
end;
begin
Forking;
writeln('Close parent process.');
writeln('Bye.');
end.
Example 3.1: Fork

As you see, fork copies the parent process. The child process starts to execute after the fork call. The differences between the parent process and the child process are only:
* The child has a different process identifier.
* The child has a different parent process.
* File locks are not inherited.
* Pending signals are not inherited.

Exec

Since we have now a new child process it is necessary to replace that process image with a new one:

function execve(PathName: PChar; const argv: PPChar; const envp: PPChar): Integer;
function execv(PathName: PChar; const argv: PPChar): Integer;

The execve function loads and executes PathName. The arguments are stored in argv and the environment is stored in envp.
execv performs the same actions as execve, except that the environment is taken from the "environ" setting.

program ForkExecDemo;
{$APPTYPE CONSOLE}
uses
Libc;
procedure ForkAndExec;
var
pid: PID_T;
parg: array[1..3] of PChar;
filetoplay: string;
begin
pid := fork;
if pid = 0 then
begin
filetoplay := 'test.wav';
parg[1] := 'playwave';
parg[2] := PChar(FileToPlay);
parg[3] := nil;
if execv('/usr/bin/playwave',PPChar(@parg[1])) = -1 then
begin
writeln('Something is wrong in the sub process.');
Halt;
end;
end;
writeln('Hello. I am the parent process. I am going to wait some time...');
__sleep(3);
end;
begin
ForkAndExec;
writeln('Close parent process.');
writeln('Bye.');
end.
Example 3.2: Fork and Exec

While this works great in console applications you may get problems in X applications because fork copies all the file descriptors. In such a case you'll have to close them within the child process. Example 3.3 illustrates this:

procedure ForkAndExec;
var
pid: PID_T;
Max: Integer;
I: Integer;
begin
pid := fork;
if pid = 0 then
begin
Max := sysconf(_SC_OPEN_MAX);
for i := (STDERR_FILENO+1) toMax do
begin
fcntl(i, F_SETFD, FD_CLOEXEC);
end;
[...]
end;
[...]
end;
Example 3.3: Closing the file descriptors

Sometimes you may want to let the parent process wait until the child process has been terminated. This can be done using the function waitpid. It is declared as:

function waitpid(__pid: pid_t; __stat_loc: PInteger; __options: Integer): pid_t; cdecl;
__pid: The process to wait for.
__stat_loc: The status of the terminated app. Can be nil.
__options: Zero,a flag or an combination of the flags WNOHANG and WUNTRACED. WNOHANG doesn't let the parent wait until the child has finished while WUNTRACED delivers status information not only from terminated applications but also from stopped ones. None of the flags are of interest for our purposes.
Example 3.4 illustrates the usage of the function WaitPid. It uses fork and execvp.

function StartApp(name: string; arguments: array of string): Integer;
var
pid: PID_T;
Max: Integer;
I: Integer;
parg: PPCharArray;
argnum: Integer;
begin
Result := -1;
pid := fork;
if pid = 0 then
begin
Max := sysconf(_SC_OPEN_MAX);
for i := (STDERR_FILENO+1) to Max do
begin
fcntl(i, F_SETFD, FD_CLOEXEC);
end;
argnum := High(Arguments) + 1;
GetMem(parg,(2 + argnum) * sizeof(PChar));
parg[0] := PChar(Name);
i := 0;
while i <= high(arguments) do
begin
inc(i);
parg[i] := PChar(arguments[i-1]);
end;
parg[i+1] := nil;
execvp(PChar(name),PPChar(@parg[0]));
halt;
end;
if pid > 0 then
begin
waitpid(pid,@Result,0);
end;
end;
Example 3.4: StartApp function
:cool:

pkbza
13-01-2006, 02:45:32
Gracias, veré que puedo hacer con eso, saludos.