I'm working with TProcess, which for the most part I love, but I'm scratching my head right now because of a problem I'm experiencing.
Suppose I have the following command: ifconfig | grep -E -e "inet6? addr:" | grep -v -E -e "(127.0.0.1|::1|Scope:Link)" | sed -e "s/^.*addr:\s*\(\S*\)\(\/\d+\)*.*$/\1/g" I can run that as-is from the command-line, since my shell hooks up all of the pipes and whatnot for me. Also, I can execute it like this: bash -c "ifconfig | grep -E -e \"inet6? addr:\" | grep -v -E -e \"(127.0.0.1|::1|Scope:Link)\" | sed -e \"s/^.*addr:\s*\(\S*\)\(\/\d+\)*.*$/\1/g\"" By escaping the double-quotes and then passing the whole thing to bash as the argument for the -c, it works. However, if I pass the above command (the one using bash -c with the double-quotes of the original command escaped), as the CommandLine of a TProcess, it doesn't work. TProcess execs bash, but the argument doesn't seem to make it there correctly. Bash spits out: $ ./tprocquotes addr:\" | grep -v -E -e \"(127.0.0.1|::1|Scope:Link)\" | sed -e \"s/^.*addr:\s*\(\S*\)\(\/\d+\)*.*$/\1/g\"": -c: line 0: unexpected EOF while looking for matching `"' addr:\" | grep -v -E -e \"(127.0.0.1|::1|Scope:Link)\" | sed -e \"s/^.*addr:\s*\(\S*\)\(\/\d+\)*.*$/\1/g\"": -c: line 1: syntax error: unexpected end of file Yes, I know I could use single quotes instead of double quotes to the -c argument, and then I wouldn't have to escape the double-quotes. And, yes, I know there are other ways to accomplish what that command does by parsing the output of ifconfig. I've got workarounds for this particular example, but I still don't know why it shouldn't work with TProcess with me escaping the double-quotes with a backslash and then passing the whole thing double-quote enclosed to bash -c as the TProcess CommandLine. What am I doing wrong? I've got an example program illustrating what I'm doing on PasteBin at http://pastebin.com/f336d384b, and I'll also include the contents of the program in this email in case anyone in the future's ever searching the archives and the pastebin link doesn't exist any more. -SG ----------------------------------------------------------------------------------------------------------------- program tprocquotes; {$mode objfpc}{$H+} uses {$IFDEF UNIX} cthreads, {$ENDIF} Classes, SysUtils, dateutils, process, pipes; function ExecuteProcess(const command : string; out commandExitCode : integer; const timeoutSec : integer; const stdout_stream : TStream; const stderr_stream : TStream; const redirectErr : boolean) : boolean; const READ_BYTES = 2048; var P : TProcess; buffer : array of byte; tmpStdOutput : TMemoryStream; tmpStdError : TMemoryStream; stdOutToUse : TStream; stdErrToUse : TStream; startTime : TDateTime; stdOutBytesRead : longword; function ReadAvailableBytes(const source_stream : TInputPipeStream; const dest_stream : TStream) : longword; var bytes_available : longword; bytes_read : longint; bytes_to_read : longint; begin result := 0; if Assigned(source_stream) and Assigned(dest_stream) then begin bytes_available := source_stream.NumBytesAvailable; while (bytes_available > 0) do begin if (bytes_available >= READ_BYTES) then begin bytes_to_read := READ_BYTES; end else begin bytes_to_read := bytes_available; end; bytes_read := source_stream.Read(buffer[0], bytes_to_read); if (bytes_read > 0) then begin dest_stream.Write(buffer[0], bytes_read); result := result + longword(bytes_read); end; bytes_available := source_stream.NumBytesAvailable; end; end; end; begin result := true; commandExitCode := 1; if Assigned(stdout_stream) then begin tmpStdOutput := nil; stdOutToUse := stdout_stream; end else begin tmpStdOutput := TMemoryStream.Create; stdOutToUse := tmpStdOutput; end; stdOutToUse.Size := 0; stdOutToUse.Seek(0, soFromBeginning); if Assigned(stderr_stream) then begin tmpStdError := nil; stdErrToUse := stderr_stream; end else begin tmpStdError := TMemoryStream.Create; stdErrToUse := tmpStdError; end; stdErrToUse.Size := 0; stdErrToUse.Seek(0, soFromBeginning); SetLength(buffer, READ_BYTES); P := TProcess.Create(nil); try P.CommandLine := command; P.Options := [poUsePipes]; if redirectErr then P.Options := P.Options + [poStderrToOutPut]; P.InheritHandles := false; startTime := now; P.Execute; { read stdout while the process runs } while P.Running do begin stdOutBytesRead := ReadAvailableBytes(P.Output, stdOutToUse); if (stdOutBytesRead = 0) and (ReadAvailableBytes(P.Stderr, stdErrToUse) = 0) then begin sleep(100); end; { check timeout } if (timeoutSec > 0) and (SecondsBetween(now, startTime) > timeoutSec) then begin result := false; P.Terminate(1); P.WaitOnExit; // avoid a zombie process break; end; end; commandExitCode := P.ExitStatus; { read what's left over in stdout } ReadAvailableBytes(P.Output, stdOutToUse); { read what's left over in stderr } ReadAvailableBytes(P.Stderr, stdErrToUse); finally if Assigned(tmpStdError) then FreeAndNil(tmpStdError); if Assigned(tmpStdOutput) then FreeAndNil(tmpStdOutput); FreeAndNil(P); SetLength(buffer, 0) end; end; const COMMAND = 'bash -c "ifconfig | grep -E -e \"inet6? addr:\" | grep -v -E -e \"(127.0.0.1|::1|Scope:Link)\" | sed -e \"s/^.*addr:\s*\(\S*\)\(\/\d+\)*.*$/\1/g\""'; var stdOutStream : THandleStream; stdErrStream : THandleStream; commandExitCode : integer; begin stdOutStream := THandleStream.Create(StdOutputHandle); stdErrStream := THandleStream.Create(StdErrorHandle); try if ExecuteProcess(COMMAND, commandExitCode, 120, stdOutStream, stdErrStream, false) then begin ExitCode := commandExitCode; end else begin ExitCode := 1; end; finally FreeAndNil(stdOutStream); FreeAndNil(stdErrStream); end; end. ----------------------------------------------------------------------------------------------------------------- -- This email is fiction. Any resemblance to actual events or persons living or dead is purely coincidental. Seth Grover sethdgrover[at]gmail[dot]com _______________________________________________ fpc-devel maillist - fpc-devel@lists.freepascal.org http://lists.freepascal.org/mailman/listinfo/fpc-devel