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.