2

I am on OSX and I found the following Delphi (Firemonkey) code to write the console output to a Memo. This works fine when I am using normal commands like "ls", but it doesn't capture the output from external terminal apps.

For example, if I run the command line application "youtube-dl", the output shows up only in the PAServer log, but not in the Memo.

Is there a way to do this? Or can someone modify the code to make this work?

const
  libc = '/usr/lib/libc.dylib';

type
  PIOFile = Pointer;

//Create a new stream connected to a pipe running the given command.
function popen(const Command: PAnsiChar; Modes: PAnsiChar): PIOFile; cdecl;
  external libc name '_popen';

//Close a stream opened by popen and return the status of its child.
function pclose(Stream: PIOFile): Integer; cdecl; external libc name '_pclose';

//Return the EOF indicator for STREAM.
function feof(Stream: PIOFile): Integer; cdecl; external libc name '_feof';

//Read chunks of generic data from STREAM.
function fread(Ptr: Pointer; Size: LongWord; N: LongWord;
  Stream: PIOFile): LongWord; cdecl; external libc name '_fread';

//Wait for a child to die.  When one does, put its status in *STAT_LOC
//and return its process ID.  For errors, return (pid_t) -1.
function wait(__stat_loc: PInteger): Integer; cdecl;
  external libc name '_wait';

procedure TForm1.ExecCmdine(const CmdLine: string);
var
  Output: PIOFile;
  Buffer: PAnsiChar;
  TempString: Ansistring;
  Line: AnsiString;
  BytesRead: Integer;
const
  BufferSize: Integer = 1000;
begin
  TempString := '';
  Output := popen(PAnsiChar(Ansistring(CmdLine)), 'r');
  GetMem(Buffer, BufferSize);
  if Assigned(Output) then
  try
    while feof(Output) = 0 do
    begin
      BytesRead := fread(Buffer, 1, BufferSize, Output);
      SetLength(TempString, Length(TempString) + BytesRead);
      Move(Buffer^, TempString[length(TempString) - (BytesRead - 1)], BytesRead);

      while Pos(#10, TempString) > 0 do
      begin
        Line := Copy(TempString, 1, Pos(#10, TempString) - 1);
          Memo1.Lines.Add(UTF8ToString(Line));

        TempString := Copy(TempString, Pos(#10, TempString) + 1, Length(TempString));
      end;
    end;
  finally
    pclose(output);
    wait(nil);
    FreeMem(Buffer, BufferSize);
  end;
end;
14
  • Does anything come from output? If it does... Why does necessary cmdresult? Doesn't utf8tostring pass back an empty string? (due to invalid char codes)? Do unit tests for all branches. Commented Mar 25, 2017 at 20:47
  • the command I run (youtube-dl) works and it downloads the video, but I want to do different things with the console output. For example a process bar or I want to see the available video formats, and so on. I don't think the utf8tostring is the problem because I added a showmessage('test') in the while loop at position "while Pos(#10, TempString) > 0 do" for test purposes and it never shows up. so the while loop seems to be never true. Commented Mar 25, 2017 at 21:07
  • made the concatenation simpler. tempString := tempString + strPas( buffer^ ) Commented Mar 25, 2017 at 21:16
  • if the replacestring function available in you compiler : replaceString( tempString, CONST_chars_NewLine, '', [rfReplaceAll, rfIgnoreCase] ); Commented Mar 25, 2017 at 21:20
  • 3
    Are you sure youtube-dl prints its output to stdout? Maybe it goes to stderr, which popen doesn't capture. You can redirect stderr to stdout (like with 2>&1), or you can use some other method of executing the program and capturing its output from both output streams separately. Commented Mar 31, 2017 at 16:21

1 Answer 1

2

Rob Kennedy had the correct answer, but sadly he didn't post it as answer, so I will do it.

The problem was that the console output of youtube-dl gets printed to stderr and not stdout, so I had to add 2>&1 to the console command when running it.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.