FreePascal调用外部shell时怎么处理sudo指令?

FreePascal可以调用外部shell指令,如:

program LargeOutputDemo;
 
{$mode objfpc}{$H+}
 
uses
  Classes, SysUtils, Process; // Process is the unit that holds TProcess
 
const
  BUF_SIZE = 2048; // Buffer size for reading the output in chunks
 
var
  AProcess     : TProcess;
  OutputStream : TStream;
  BytesRead    : longint;
  Buffer       : array[1..BUF_SIZE] of byte;
 
begin
  // Set up the process; as an example a recursive directory search is used
  // because that will usually result in a lot of data.
  AProcess := TProcess.Create(nil);
 
  // The commands for Windows and *nix are different hence the $IFDEFs
  {$IFDEF Windows}
    // In Windows the dir command cannot be used directly because it's a build-in
    // shell command. Therefore cmd.exe and the extra parameters are needed.
    AProcess.Executable := 'c:\windows\system32\cmd.exe';
    AProcess.Parameters.Add('/c');
    AProcess.Parameters.Add('dir /s c:\windows');
  {$ENDIF Windows}
 
  {$IFDEF Unix}
    AProcess.Executable := '/bin/ls';
 
    {$IFDEF Darwin}
      AProcess.Parameters.Add('-recursive');
      AProcess.Parameters.Add('-all');
    {$ENDIF Darwin}
 
    {$IFDEF Linux}
      AProcess.Parameters.Add('--recursive');
      AProcess.Parameters.Add('--all');
    {$ENDIF Linux}
 
    {$IFDEF FreeBSD}
      AProcess.Parameters.Add('-R');
      AProcess.Parameters.Add('-a');
    {$ENDIF FreeBSD}
 
    AProcess.Parameters.Add('-l');
  {$ENDIF Unix}
 
  // Process option poUsePipes has to be used so the output can be captured.
  // Process option poWaitOnExit can not be used because that would block
  // this program, preventing it from reading the output data of the process.
  AProcess.Options := [poUsePipes];
 
  // Start the process (run the dir/ls command)
  AProcess.Execute;
 
  // Create a stream object to store the generated output in. This could
  // also be a file stream to directly save the output to disk.
  OutputStream := TMemoryStream.Create;
 
  // All generated output from AProcess is read in a loop until no more data is available
  repeat
    // Get the new data from the process to a maximum of the buffer size that was allocated.
    // Note that all read(...) calls will block except for the last one, which returns 0 (zero).
    BytesRead := AProcess.Output.Read(Buffer, BUF_SIZE);
 
    // Add the bytes that were read to the stream for later usage
    OutputStream.Write(Buffer, BytesRead)
 
  until BytesRead = 0;  // Stop if no more data is available
 
  // The process has finished so it can be cleaned up
  AProcess.Free;
 
  // Now that all data has been read it can be used; for example to save it to a file on disk
  with TFileStream.Create('output.txt', fmCreate) do
  begin
    OutputStream.Position := 0; // Required to make sure all data is copied from the start
    CopyFrom(OutputStream, OutputStream.Size);
    Free
  end;
 
  // Or the data can be shown on screen
  with TStringList.Create do
  begin
    OutputStream.Position := 0; // Required to make sure all data is copied from the start
    LoadFromStream(OutputStream);
    writeln(Text);
    writeln('--- Number of lines = ', Count, '----');
    Free
  end;
 
  // Clean up
  OutputStream.Free;
end.

但是如果使用了sudo,那么运行是不成功的,比如:

program rootls;

{ Demonstrates using TProcess, redirecting stdout/stderr to our stdout/stderr,
calling sudo on FreeBSD/Linux/macOS, and supplying input on stdin}
{$mode objfpc}{$H+}

uses
  Classes,
  Math, {for min}
  Process;

  procedure RunsLsRoot;
  var
    Proc: TProcess;
    CharBuffer: array [0..511] of char;
    ReadCount: integer;
    ExitCode: integer;
    SudoPassword: string;
  begin
    WriteLn('Please enter the sudo password:');
    Readln(SudoPassword);
    ExitCode := -1; //Start out with failure, let's see later if it works
    Proc := TProcess.Create(nil); //Create a new process
    try
      Proc.Options := [poUsePipes, poStderrToOutPut]; //Use pipes to redirect program stdin,stdout,stderr
      Proc.CommandLine := 'sudo -S ls /root'; //Run ls /root as root using sudo
      // -S causes sudo to read the password from stdin.
      Proc.Execute; //start it. sudo will now probably ask for a password

      // write the password to stdin of the sudo program:
      SudoPassword := SudoPassword + LineEnding;
      Proc.Input.Write(SudoPassword[1], Length(SudoPassword));
      SudoPassword := '%*'; //short string, hope this will scramble memory a bit; note: using PChars is more fool-proof
      SudoPassword := ''; // and make the program a bit safer from snooping?!?

      // main loop to read output from stdout and stderr of sudo
      while Proc.Running or (Proc.Output.NumBytesAvailable > 0) or
        (Proc.Stderr.NumBytesAvailable > 0) do
      begin
        // read stdout and write to our stdout
        while Proc.Output.NumBytesAvailable > 0 do
        begin
          ReadCount := Min(512, Proc.Output.NumBytesAvailable); //Read up to buffer, not more
          Proc.Output.Read(CharBuffer, ReadCount);
          Write(StdOut, Copy(CharBuffer, 0, ReadCount));
        end;
        // read stderr and write to our stderr
        while Proc.Stderr.NumBytesAvailable > 0 do
        begin
          ReadCount := Min(512, Proc.Stderr.NumBytesAvailable); //Read up to buffer, not more
          Proc.Stderr.Read(CharBuffer, ReadCount);
          Write(StdErr, Copy(CharBuffer, 0, ReadCount));
        end;
      end;
      ExitCode := Proc.ExitStatus;
    finally
      Proc.Free;
      Halt(ExitCode);
    end;
  end;

begin
  RunsLsRoot;
end.

这段代码编译后执行,会提示输入root口令,但是没法验证成功。

暂时没有找到解决问题的办法,难道真的没办法吗? 

找到解决的办法了,参见这篇文档:怎样使用sudo的时候不需要输入密码?-CSDN博客

核心思想就是,设置该用户sudo时不需要输入密码,即在sudoers文件中,将原来test账户的设置

test ALL=(ALL) 

修改为:

test ALL=(ALL) NOPASSWD: ALL 

这样这个test账户再sudo的时候,就不需要输入指令了。使用FreePascal这段代码测试成功:

program LargeOutputDemo;
 
{$mode objfpc}{$H+}
 
uses
  Classes, SysUtils, Process; // Process is the unit that holds TProcess
 
const
  BUF_SIZE = 2048; // Buffer size for reading the output in chunks
 
var
  AProcess     : TProcess;
  OutputStream : TStream;
  BytesRead    : longint;
  Buffer       : array[1..BUF_SIZE] of byte;
 
begin
  // Set up the process; as an example a recursive directory search is used
  // because that will usually result in a lot of data.
  AProcess := TProcess.Create(nil);
 
  // The commands for Windows and *nix are different hence the $IFDEFs
  {$IFDEF Windows}
    // In Windows the dir command cannot be used directly because it's a build-in
    // shell command. Therefore cmd.exe and the extra parameters are needed.
    AProcess.Executable := 'c:\windows\system32\cmd.exe';
    AProcess.Parameters.Add('/c');
    AProcess.Parameters.Add('dir /s c:\windows');
  {$ENDIF Windows}
 
  {$IFDEF Unix}
    // AProcess.Executable := '/bin/ls';
    AProcess.Executable := '/usr/bin/sudo';
    AProcess.Parameters.Add('ls');
    // AProcess.Executable := 'ls';
 
    {$IFDEF Darwin}
      AProcess.Parameters.Add('-recursive');
      AProcess.Parameters.Add('-all');
    {$ENDIF Darwin}
 
    {$IFDEF Linux}
      AProcess.Parameters.Add('--recursive');
      AProcess.Parameters.Add('--all');
    {$ENDIF Linux}
 
    {$IFDEF FreeBSD}
      AProcess.Parameters.Add('-R');
      AProcess.Parameters.Add('-a');
    {$ENDIF FreeBSD}
 
    AProcess.Parameters.Add('-l');
  {$ENDIF Unix}
 
  // Process option poUsePipes has to be used so the output can be captured.
  // Process option poWaitOnExit can not be used because that would block
  // this program, preventing it from reading the output data of the process.
  AProcess.Options := [poUsePipes];
 
  // Start the process (run the dir/ls command)
  AProcess.Execute;
 
  // Create a stream object to store the generated output in. This could
  // also be a file stream to directly save the output to disk.
  OutputStream := TMemoryStream.Create;
 
  // All generated output from AProcess is read in a loop until no more data is available
  repeat
    // Get the new data from the process to a maximum of the buffer size that was allocated.
    // Note that all read(...) calls will block except for the last one, which returns 0 (zero).
    BytesRead := AProcess.Output.Read(Buffer, BUF_SIZE);
 
    // Add the bytes that were read to the stream for later usage
    OutputStream.Write(Buffer, BytesRead)
 
  until BytesRead = 0;  // Stop if no more data is available
 
  // The process has finished so it can be cleaned up
  AProcess.Free;
 
  // Now that all data has been read it can be used; for example to save it to a file on disk
  with TFileStream.Create('output.txt', fmCreate) do
  begin
    OutputStream.Position := 0; // Required to make sure all data is copied from the start
    CopyFrom(OutputStream, OutputStream.Size);
    Free
  end;
 
  // Or the data can be shown on screen
  with TStringList.Create do
  begin
    OutputStream.Position := 0; // Required to make sure all data is copied from the start
    LoadFromStream(OutputStream);
    writeln(Text);
    writeln('--- Number of lines = ', Count, '----');
    Free
  end;
 
  // Clean up
  OutputStream.Free;
end.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值