[Lazarus] Run 3 extrenal program, pipeline

Sven Barth pascaldragon at googlemail.com
Thu Feb 2 15:30:47 CET 2012


Am 02.02.2012 12:00, schrieb xmldom at seznam.cz:
> Hi, I need run 3 external program and redirected output: first | second | third
>
> For example:
>     df -hT | grep -v -E 'tmpfs|ecryptfs' | grep 'sda2'
> it gives
>     /dev/sda2     ext4     19G   11G  7,6G  58% /
>
> I use this http://wiki.lazarus.freepascal.org/Executing_External_Programs#How_to_redirect_output_with_TProcess sample code. It works fine for  first | second.
>
> For three programs I try edit it:
>
>
> function threePipe(inputA, inputB, inputC: string): string;
> var
>   FirstProcess, SecondProcess, ThirdProcess: TProcess;
>   Buffer: array[0..127] of char;
>   ReadCount: Integer;
>   ReadSize: Integer;
> begin
>   FirstProcess  := TProcess.Create(nil);
>   SecondProcess := TProcess.Create(nil);
>   ThirdProcess := TProcess.Create(nil);
>
>   FirstProcess.Options  := [poUsePipes];
>   SecondProcess.Options := [poUsePipes];
>   ThirdProcess.Options  := [poUsePipes,poStderrToOutPut];
>
>   FirstProcess.CommandLine  := inputA;
>   SecondProcess.CommandLine := inputB;
>   ThirdProcess.CommandLine := inputC;
>
>   FirstProcess.Execute;
>   SecondProcess.Execute;
>   ThirdProcess.Execute;
>
>   while FirstProcess.Running or (FirstProcess.Output.NumBytesAvailable>  0) do
>   begin
>     if FirstProcess.Output.NumBytesAvailable>  0 then
>     begin
>       ReadSize := FirstProcess.Output.NumBytesAvailable;
>       if ReadSize>  SizeOf(Buffer) then
>         ReadSize := SizeOf(Buffer);
>       ReadCount := FirstProcess.Output.Read(Buffer[0], ReadSize);
>       SecondProcess.Input.Write(Buffer[0], ReadCount);
>     end;
>   end;
>   SecondProcess.CloseInput;
>
>   while SecondProcess.Running or (SecondProcess.Output.NumBytesAvailable>  0) do
>   begin
>     if SecondProcess.Output.NumBytesAvailable>  0 then
>     begin
>       ReadSize := SecondProcess.Output.NumBytesAvailable;
>       if ReadSize>  SizeOf(Buffer) then
>         ReadSize := SizeOf(Buffer);
>       ReadCount := SecondProcess.Output.Read(Buffer[0], ReadSize);
>       ThirdProcess.Input.Write(Buffer[0], ReadCount);
>     end;
>   end;
>   ThirdProcess.CloseInput;
>
>
>   while ThirdProcess.Running do
>     Sleep(1);
>   ReadSize := ThirdProcess.Output.NumBytesAvailable;
>   if ReadSize>  SizeOf(Buffer) then
>     ReadSize := SizeOf(Buffer);
>   if ReadSize>  0 then
>   begin
>     ReadCount := ThirdProcess.Output.Read(Buffer, ReadSize);
>     //WriteLn(Copy(Buffer,0, ReadCount));
>   end
>   else
>     WriteLn('grep did not find what we searched for. ', ThirdProcess.ExitStatus);
>
>   FirstProcess.Free;
>   SecondProcess.Free;
>   ThirdProcess.Free;
>   threePipe := Copy(Buffer,0, ReadCount);
> end;
>
>
>
> But after i call
>     Label1.Caption:=threePipe('df -hT', 'grep -v -E ''tmpfs|ecryptfs''', 'grep ''sda2''');
> function my application end with infinite loop.
>
> Where I do an error?

You need to read from all three processes in one loop as they will block 
if they've reached the limit of the output pipe. Try something like this 
(pseudo code):

while FirstProcess.Running or SecondProcess.Running or 
ThirdProcess.Running do begin
   FromFirstToSecond;
   FromSecondToThird;
   FromThirdToOutput;
end;
FromThirdToOutput; // <= you need to process the leftover output from 
the process as well

It might be more complex than the above, but in essence this should do 
it (maybe you'll need to play with the buffer sizes, etc).

Regards,
Sven




More information about the Lazarus mailing list