【问题标题】:Execute external program from Linux/Delphi 10.2 console application从 Linux/Delphi 10.2 控制台应用程序执行外部程序
【发布时间】:2017-11-08 22:46:23
【问题描述】:

如何从在 Delphi 10.2 Tokyo 中创建的 Linux 控制台应用程序执行外部程序?

我想要做的是执行一个带有参数的shell命令

/home/test/qrencode -o /tmp/abc.png '08154711'

我不需要程序的输出,但应该同步执行。

在 Windows 环境中这很容易,但由于 Delphi(Kylix 之后)对 64 位 Linux 的支持是相当新的,我现在在 Web 上找不到任何提示。

非常感谢任何帮助我解决问题的提示。

提前致谢!

【问题讨论】:

  • popen是常用的方法
  • 非常感谢!搜索 popen 和 delphi 让我找到了很多例子,比如chapmanworld.com/2017/04/06/calling-linux-commands-from-delphi...
  • 这更多是关于 Linux 而不是 Delphi。对于此类问题,请搜索 Linux 和 C 或 C++ 主题。翻译很容易。精通阅读 C 语言会让您受益匪浅,因为您不会找到很多针对 Linux 平台主题的 Delphi 特定示例。
  • @Victoria 这是一个平台服务。语言不可知论者。就像 Windows 上的 CreateProcess 不是原生的 Delphi 函数一样。
  • @Victoria 我是务实和现实的。你可以是纯粹主义者和理想主义者,但如果你想要一个解决方案,你会怎么做?等待有人创建一个跨平台的 Delphi 包装器来抽象出平台进程创建的差异?或者现在使用平台 API 执行此操作。对于这样的问题,能够阅读 C 并能够找到针对 C 的资源是值得的,C 是本机平台语言。然后将它们映射到 Delphi。

标签: linux delphi


【解决方案1】:

Davids 的提示向我指出了一个有助于创建解决方案的示例。最棘手的部分是找出如何将 Delphi 字符串转换为 MarshaledAString,因为该示例使用 const 字符串作为 popen 的参数。我在 RHEL 7.3 上进行了测试,运行起来非常棒。

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

type
  TStreamHandle = pointer;

function popen(const command: MarshaledAString; const _type: MarshaledAString): TStreamHandle; cdecl;
      external libc name _PU + 'popen';
function pclose(filehandle: TStreamHandle): int32; cdecl; external libc name _PU + 'pclose';
function fgets(buffer: pointer; size: int32; Stream: TStreamHandle): pointer; cdecl; external libc name _PU + 'fgets';

function runCommand(const acommand: MarshaledAString): String;
// run a linux shell command and return output
// Adapted from http://chapmanworld.com/2017/04/06/calling-linux-commands-from-delphi/
var
  handle: TStreamHandle;
  data: array [0 .. 511] of uint8;

  function bufferToString(buffer: pointer; maxSize: uint32): string;
  var
    cursor: ^uint8;
    endOfBuffer: nativeuint;
  begin
    if not assigned(buffer) then
      exit;
    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;

begin
  result := '';
  handle := popen(acommand, 'r');
  try
    while fgets(@data[0], sizeof(data), handle) <> nil do
    begin
      result := result + bufferToString(@data[0], sizeof(data));
    end;
  finally
    pclose(handle);
  end;
end;

function createQRCode(id, fn: string): string;
// Create qr-code using qrencode package
begin
  deletefile(fn);
  if fileExists(fn) then
    raise Exception.create('Old file not deleted!');
  // I am targeting rhel for now, so I know the path for sure
  result := runCommand(MarshaledAString(UTF8STring('/usr/bin/qrencode -o ' + fn + ' ''' + id + '''')));
  if not fileExists(fn) then
    raise Exception.create('New file not created!');
end;

function testqr: String;
// Test QR Code creation with error handling
// QREncode does not output anything but who knows ;-)
begin
  try
    result := createQRCode('08154711', '/tmp/myqrcode.png');
  except
    on e: Exception do
    begin
      result := 'Error: ' + e.message;
    end;
  end;
end;

【讨论】:

  • 关于我的问题下面的讨论,我想说恕我直言,这是关于 Linux 的,并且等同于 anoput Delphi,因为仅了解 linux API 调用并不能帮助我找到这个解决方案。
【解决方案2】:

我写了这段代码来完成这个任务

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

type
  TStreamHandle = pointer;

  TLinuxUtils = class
  public
    class function RunCommandLine(ACommand : string) : TStringList;overload;
    class function RunCommandLine(Acommand : string; Return : TProc<String>) : boolean; overload;
    class function findParameter(AParameter : string) : boolean;
  end;



  function popen(const command: MarshaledAString; const _type: MarshaledAString): TStreamHandle; cdecl; external libc name _PU + 'popen';
  function pclose(filehandle: TStreamHandle): int32; cdecl; external libc name _PU + 'pclose';
  function fgets(buffer: pointer; size: int32; Stream: TStreamHAndle): pointer; cdecl; external libc name _PU + 'fgets';


implementation

class function TLinuxUtils.RunCommandLine(ACommand : string) : TStringList;
var
  Handle: TStreamHandle;
  Data: array[0..511] of uint8;
  M : TMarshaller;

begin
  Result := TStringList.Create;
  try
    Handle := popen(M.AsAnsi(PWideChar(ACommand)).ToPointer,'r');
    try
      while fgets(@data[0],Sizeof(Data),Handle)<>nil do begin
        Result.Add(Copy(UTF8ToString(@Data[0]),1,UTF8ToString(@Data[0]).Length -1));//,sizeof(Data)));
      end;
    finally
      pclose(Handle);
    end;
  except
    on E: Exception do
      Result.Add(E.ClassName + ': ' + E.Message);
  end;
end;

class function TLinuxUtils.RunCommandLine(Acommand : string; Return : TProc<string>) : boolean;
var
  Handle: TStreamHandle;
  Data: array[0..511] of uint8;
  M : TMarshaller;

begin
  Result := false;
  try
    Handle := popen(M.AsAnsi(PWideChar(ACommand)).ToPointer,'r');
    try
      while fgets(@data[0],Sizeof(Data),Handle)<>nil do begin
        Return(Copy(UTF8ToString(@Data[0]),1,UTF8ToString(@Data[0]).Length -1));//,sizeof(Data)));
      end;
    finally
      pclose(Handle);
    end;
  except
    on E: Exception do
      Return(E.ClassName + ': ' + E.Message);
  end;
end;

class function TLinuxUtils.findParameter(AParameter : string) : boolean;
var
  I : Integer;
begin
  Result := false;
  for I := 0 to Pred(ParamCount) do
  begin
    Result := AParameter.ToUpper = ParamStr(i).ToUpper;
    if Result then
      Break;
  end;
end;

您不必担心 MarshaledString。 RunCommandLine 函数有两种调用方式。首先,您可以在 TStringList 上返回控制台将返回的所有行。 第二个您可以传递一个匿名方法,该方法将逐行处理命令行的返回。

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多