Calling Linux Commands from Delphi

One of the great features of Linux is that you can do just about anything from the command line. If we’re able to gain access to command line instructions from our Delphi applications, this will give us a very powerful API for the system. In this video I’m going to show you how to access the Linux command line from your Delphi applications.

*note* In this video I am assuming you are already configured to deploy an application to Linux and understand how to launch it from the Linux command line. If this is not the case, see my earlier posts covering these subjects: 

Best Viewed Full Screen:

Here is the source code for the ‘myls’ application featured in the video…

program myls;
{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.SysUtils,
  Posix.Base,
  Posix.Fcntl;

type
  TStreamHandle = pointer;

///  <summary>
///    Man Page: http://man7.org/linux/man-pages/man3/popen.3.html
///  </summary>
function popen(const command: MarshaledAString; const _type: MarshaledAString): TStreamHandle; cdecl; external libc name _PU + 'popen';

///  <summary>
///    Man Page: http://man7.org/linux/man-pages/man3/pclose.3p.html
///  </summary>
function pclose(filehandle: TStreamHandle): int32; cdecl; external libc name _PU + 'pclose';

///  <summary>
///    Man Page: http://man7.org/linux/man-pages/man3/fgets.3p.html
///  </summary>
function fgets(buffer: pointer; size: int32; Stream: TStreamHAndle): pointer; cdecl; external libc name _PU + 'fgets';

///  <summary>
///    Utility function to return a buffer of ASCII-Z data as a string.
///  </summary>
function BufferToString( Buffer: pointer; MaxSize: uint32 ): string;
var
  cursor: ^uint8;
  EndOfBuffer: nativeuint;
begin
  Result := '';
  if not assigned(Buffer) then begin
    exit;
  end;
  cursor := Buffer;
  EndOfBuffer := NativeUint(cursor) + MaxSize;
  while (NativeUint(cursor)<EndOfBuffer) and (cursor^<>0) do begin
    Result := Result + chr(cursor^);
    cursor := pointer( succ(NativeUInt(cursor)) );
  end;
end;

var
  Handle: TStreamHandle;
  Data: array[0..511] of uint8;

begin
  try
    Handle := popen('/bin/ls -lart','r');
    try
      while fgets(@data[0],Sizeof(Data),Handle)<>nil do begin
        Write(BufferToString(@Data[0],sizeof(Data)));
      end;
    finally
      pclose(Handle);
    end;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

 

Facebooktwittergoogle_plusredditpinterestlinkedintumblrmail

3 Responses

  1. The simplest solution for UTF8 is to completely replace the BufferToString() function with the Delphi system function Utf8ToString()….

    while fgets(@data[0],Sizeof(Data),Handle)<>nil do begin
    Write(Utf8ToString(@Data[0]));
    end;

    However, Utf8ToString() does not have a character limiter to stop it from overrunning the end of the buffer. In order to resolve this you would need to pass a pre-zero’d buffer into fGets() and pass the size of the buffer less one byte (to protect the terminating zero). Or otherwise, simply pass a buffer large enough for the content + zero byte.

  2. andrew.sovtsov@embarcadero.com says:

    Hi Craig,
    Nice post and very useful one!
    In practice your BufferToString function works incorrectly with non-ascii symbols (e.g. UTF8)

  1. 2017-05-18

    […] In this post, as in the webinar, I’m referencing code from my earlier blog post on launching external applications under linux: http://chapmanworld.com/2017/04/06/calling-linux-commands-from-delphi/ […]

Leave a Reply