Difference between revisions of "Executing External Programs/ru"
(→Notes) |
|||
Line 541: | Line 541: | ||
Вот и все. Теперь вы можете перенаправлять вывод из одной программы в другую. | Вот и все. Теперь вы можете перенаправлять вывод из одной программы в другую. | ||
− | ==== | + | ==== Заметки ==== |
− | + | Этот пример может показаться излишним, поскольку можно запускать «сложные» команды, используя оболочку с TProcess, например: | |
<syntaxhighlight lang=pascal>Process.Commandline := 'sh -c "pwd | grep / -"';</syntaxhighlight> | <syntaxhighlight lang=pascal>Process.Commandline := 'sh -c "pwd | grep / -"';</syntaxhighlight> | ||
− | + | Но наш пример более кроссплатформенный, так как он не нуждается в модификации для работы в Windows или Linux и т.д. "sh" может существовать или не существовать на вашей платформе и обычно доступен только на платформах *nix. Кроме того, у нас больше гибкости в нашем примере, так как вы можете читать и записывать из/в ввода, вывода и stderr каждого процесса в отдельности, что может быть очень выгодно для вашего проекта. | |
===Redirecting input and output and running under root=== | ===Redirecting input and output and running under root=== |
Revision as of 23:51, 4 August 2019
│
Deutsch (de) │
English (en) │
español (es) │
français (fr) │
italiano (it) │
日本語 (ja) │
Nederlands (nl) │
polski (pl) │
português (pt) │
русский (ru) │
slovenčina (sk) │
中文(中国大陆) (zh_CN) │
Введение: сравнение
В библиотеках RTL, FCL, LCL есть разные способы выполнить внешнюю программу.
Метод | Библиотека | Платформы | Одной строкой? | Особенности |
---|---|---|---|---|
ExecuteProcess | RTL | кроссплатформенный | Да | Очень ограничен, синхронен |
ShellExecute | WinAPI | Только MS Windows | Да | Много. Может запускать программы с правами администратора |
fpsystem, fpexecve | Unix | Unix only | ||
TProcess | FCL | кроссплатформенный | Нет | Все |
RunCommand | FCL | кроссплатформенный Требуется FPC 2.6.2+ | Да | Покрывает стандартное использование TProcess |
OpenDocument | LCL | кроссплатформенный | Да | Только открывает документ. Документ будет открыт стандартной программой |
Если вы использовали ShellExecute и/или WinExec в Delphi, то вы можете начать использовать TProcess как альтернативу в FPC/Lazarus (это верно и в случае использования Lazarus на Linux, потому что TProcess является кроссплатформенным компонентом).
(Process.)RunCommand
- RunCommand документация
- RunCommandInDir докуметация
В FPC 2.6.2, некоторые вспомогательные функции для TProcess были добавлены в модуль process основанный на обертке использованной в fpcup. Эти функции могут быть для базового и среднего уровня использования и могот захватить вывод как одной строки, так и огромного количества выводимой информации.
Простой пример:
uses Process;
...
var s : ansistring;
...
if RunCommand('/bin/bash',['-c','echo $PATH'],s) then
writeln(s);
Перегруженный вариант функции может возвращать код выхода программы. RunCommandInDir запускает программу в заданной папке.
function RunCommandIndir(const curdir:string;const exename:string;const commands:array of string;var outputstring:string; var exitstatus:integer): integer;
function RunCommandIndir(const curdir:string;const exename:string;const commands:array of string;var outputstring:string): boolean;
function RunCommand(const exename:string;const commands:array of string;var outputstring:string): boolean;
SysUtils.ExecuteProcess
Простейший путь, если вам не нужно общение с процессом - это просто использовать вызов
SysUtils.ExecuteProcess('/full/path/to/binary',['arg1','arg2']);
При использовании этого метода, приложение "повисает" до тех пор, пока вызванная программа не завершится. Это может быть полезно если пользователь должен сделать что-то перед использованием вашего приложения. Если вы хотите обойти это ограничение, то более полезный кроссплатформенный способ - это TProcess, или если ваша целевая платформа только Windows, то используйте ShellExecute.
MS Windows : CreateProcess, ShellExecute и WinExec
ShellExecute - стандартная функция MS Windows (ShellApi.h) с хорошей документацией на MSDN (читайте документацию, если вам кажется, что функция ненадежна).
uses ..., ShellApi;
// Обычный текст (возвращаемые ошибки игнорируются) :
if ShellExecute(0,nil, PChar('"C:\my dir\prog.exe"'),PChar('"C:\somepath\some_doc.ext"'),nil,1) = 0 then;
// Выполняем Batch файл:
if ShellExecute(0,nil, PChar('cmd'),PChar('/c mybatch.bat'),nil,1) = 0 then;
// Открываем командную строку в заданной папке:
if ShellExecute(0,nil, PChar('cmd'),PChar('/k cd \path'),nil,1) = 0 then;
// Открываем веб-страницу стандартным браузером с помощью'start' (через скрытую командную строку) :
if ShellExecute(0,nil, PChar('cmd'),PChar('/c start www.lazarus.freepascal.org/'),nil,0) =0 then;
// или полезную команду:
procedure RunShellExecute(const prog,params:string);
begin
// ( Handle, nil/'open'/'edit'/'find'/'explore'/'print', // 'open' не всегда требуется
// path+prog, params, working folder,
// 0=hide / 1=SW_SHOWNORMAL / 3=max / 7=min) // for SW_ constants : uses ... Windows ...
if ShellExecute(0,'open',PChar(prog),PChar(params),PChar(extractfilepath(prog)),1) >32 then; //успех
// если возвращается число в диапазоне 0..32, то значит ошибка
end;
Также есть WideChar функции - ShellExecuteExW , ShellExecuteExA в режиме AnsiChar.
В качестве fMask можно ипользовать SEE_MASK_DOENVSUBST, SEE_MASK_FLAG_NO_UI или SEE_MASK_NOCLOSEPROCESS и тд Если в Delphi вы используете ShellExecute для открытия документов, например документ Word или ссылки, то присмотритесь к open* (openurl и т.д.) модуле lclintf.
Использование ShellExecuteEx с повышенными (правами администратора) правами
Если вы хотите использовать программу с повышенными (правами администратора) правами, используйте runas как альтернативу ShellExecuteEx:
uses ShellApi, ...;
function RunAsAdmin(const Handle: Hwnd; const Path, Params: string): Boolean;
var
sei: TShellExecuteInfoA;
begin
FillChar(sei, SizeOf(sei), 0);
sei.cbSize := SizeOf(sei);
sei.Wnd := Handle;
sei.fMask := SEE_MASK_FLAG_DDEWAIT or SEE_MASK_FLAG_NO_UI;
sei.lpVerb := 'runas';
sei.lpFile := PAnsiChar(Path);
sei.lpParameters := PAnsiChar(Params);
sei.nShow := SW_SHOWNORMAL;
Result := ShellExecuteExA(@sei);
end;
procedure TFormMain.RunAddOrRemoveApplication;
begin
// Пример, открывающий панель управления с повышенными правами
RunAsAdmin(FormMain.Handle, 'rundll32.exe shell32.dll,Control_RunDLL appwiz.cpl', '');
end;
Unix fpsystem, fpexecve и shell
Эти функции являются платформозависимыми.
Учтите, что Unix.Shell версии 1.0.x устарел и удален из trunk. Используйте fpsystem.
TProcess
Вы можете использовать TProcess для запуска внешних программ. Самыми полезными вещами при этом будут:
- Платформонезависимость
- Способность читать из stdout и писать в stdin.
Важно: Вы должны определять полный путь к исполняемому файлу. Например '/bin/cp' вместо 'cp'. Если программа находится где-либо в переменной PATH, то вы можете использовать функцию FindDefaultExecutablePath из модуля LCL FileUtil.
Простейший пример
Многие типичные случаи были подготовлены в функциях Runcommand. Прежде, чем начать копировать и вставлять приведенные ниже примеры, сначала проверьте их.
Простой пример
// Демо-программа, показывающая, как можно запустить
// внешнюю программу
program launchprogram;
// Подключаем модули с требуемыми
// нам процедурами и функциями.
uses
Classes, SysUtils, Process;
// Опишем переменную "AProcess"
// типа "TProcess"
var
AProcess: TProcess;
// Здесь наша программа начинается
begin
// Создаем объект TProcess и
// присваиваем его переменной AProcess.
AProcess := TProcess.Create(nil);
// Сообщим AProcess сомандную строку для запуска
// Let's use the FreePascal compiler
AProcess.CommandLine := 'ppc386 -h';
// Необходимо описать опции программы для запуска
// Эта опция не позволит нашей программе выполнятся до тех пор, пока
// запущенная программа не закончится
AProcess.Options := AProcess.Options + [poWaitOnExit];
// Теперь AProcess знает командную строку
// и мы ее запускаем
AProcess.Execute;
// Пока ppc386 не прекратит работу, мы досюда не дойдем
AProcess.Free;
end.
Вот оно! Теперь вы научились запускать внешнюю программу изнутри вашей собственной.
Усовершенствованный пример
Это все замечательно, но как я могу получить вывод программы, которую я запустил?
Хорошо, пусть наш пример немного увеличится и теперь будет выглядеть так:
// Это демо-программа, показывающая, как запускать
// внешнюю программу и читать ее вывод
program launchprogram;
// Подключаем модули
uses
Classes, SysUtils, Process;
// Опысываем переменную "AProcess"
// И добавляем список строк TStringList для сбора данных
// из вывода программы
var
AProcess: TProcess;
AStringList: TStringList;
// Начинаем нашу программу
begin
// Создаем объект TProcess
AProcess := TProcess.Create(nil);
// Создаем объект TStringList
AStringList := TStringList.Create;
// Зададим командную строку
AProcess.CommandLine := 'ppc386 -h';
// Установим опции программы. Первая из них не позволит нашей программе
// выполнятся до тех пор, пока не закончит выполнение запущенная программа
// Также добавим опцию, которая говорит, что мы хотим прочитать
// вывод запущенной программы
AProcess.Options := AProcess.Options + [poWaitOnExit, poUsePipes];
// Теперь запускаем программу
AProcess.Execute;
// Пока запущенная программа не закончится, досюда мы не дойдем
// А теперь прочитаем вывод в список строк TStringList.
AStringList.LoadFromStream(AProcess.Output);
// Сохраним вывод в файл
AStringList.SaveToFile('output.txt');
// После сохранения файла мы можем уничтожить
// TStringList и TProcess.
AStringList.Free;
AProcess.Free;
end.
Чтение больших объемов вывода
В предыдущем примере мы ждали завершения запущенной программы. После этого мы считывали все, что программа записала в выходной поток. Но ведь может оказаться и так, что программа выведет много данных, канал заполнится и вывод остановится, при этом запустившая программа ждет завершения запущенной программы, которая в свою очередь не может завершить работу, пока не выведет все данные. Возникает коллизия, dead-lock.
Поэтому следующий пример не будет использовать опцию poWaitOnExit и будет читать данные при запущенной программе. Вывод записывается в поток, который можно позже использовать для чтения его содержимого в TStringList.
program procoutlarge;
{
Copyright (c) 2004 by Marc Weustink
Этот пример был создан в надежде быть вам полезным,
но без каких-либо подразумеваемых или явных гарантий;
}
uses
Classes, Process, SysUtils;
const
READ_BYTES = 2048;
var
S: TStringList;
M: TMemoryStream;
P: TProcess;
n: LongInt;
BytesRead: LongInt;
begin
// Мы не можем использовать poWaitOnExit не зная размер вывода
// В Linux размер выходного канала равен 2 kB. Если размер выводных
// данных больше, то мы должны считывать данные.
// Пока мы ждем, чтение невозможно. Соответственно, мы получаем
// коллизию.
//
// Используем для буфера временный поток Memorystream
M := TMemoryStream.Create;
BytesRead := 0;
P := TProcess.Create(nil);
P.CommandLine := 'ppc386 -va bogus.pp';
P.Options := [poUsePipes];
WriteLn('-- executing --');
P.Execute;
while P.Running do
begin
// Убедимся, что нам хватит места
M.SetSize(BytesRead + READ_BYTES);
// попытаемся прочитать данные
n := P.Output.Read((M.Memory + BytesRead)^, READ_BYTES);
if n > 0
then begin
Inc(BytesRead, n);
Write('.')
end
else begin
// нет данных, ждем 100 ms
Sleep(100);
end;
end;
// читаем последний блок
repeat
// убедимся, что хватает места
M.SetSize(BytesRead + READ_BYTES);
// пытаемся прочитать
n := P.Output.Read((M.Memory + BytesRead)^, READ_BYTES);
if n > 0
then begin
Inc(BytesRead, n);
Write('.');
end;
until n <= 0;
if BytesRead > 0 then WriteLn;
M.SetSize(BytesRead);
WriteLn('-- executed --');
S := TStringList.Create;
S.LoadFromStream(M);
WriteLn('-- linecount = ', S.Count, ' --');
for n := 0 to S.Count - 1 do
begin
WriteLn('| ', S[n]);
end;
WriteLn('-- end --');
S.Free;
P.Free;
M.Free;
end.
Использование ввода и вывода TProcess
Смотри демо пример на Lazarus-CCR SVN.
Некоторые подсказки при использовании TProcess
Если вы создаете кроссплатформенную программу, вы можете изменять командную строку применительно к каждой ОС использую директивы "{$IFDEF}" и "{$ENDIF}".
Например:
{...}
AProcess:TProcess.Create(nil)
{$IFDEF WIN32}
AProcess.CommandLine := 'calc.exe'; // Windows калькулятор
{$ENDIF}
{$IFDEF LINUX}
AProcess.CommandLine := 'kcalc'; // KDE калькулятор
{$ENDIF}
AProcess.Execute; // как альтернативу, вы можете использовать AProcess.Active:=True
{...}
Показ комплекта приложений на переднем плане в OS X
Вы можете запустить application bundle(пакет приложения) через TProcess, вызвав исполняемый файл внутри пакета. Например:
AProcess.Executable:='/Applications/iCal.app/Contents/MacOS/iCal';
Это запустит Calendar, но окно будет позади текущего приложения. Чтобы получить приложение на переднем плане, вы можете использовать утилиту open с параметром -n:
AProcess.Executable:='/usr/bin/open';
AProcess.Parameters.Add('-n');
AProcess.Parameters.Add('-a'); //необязательно: скрыть терминал - аналогично опции Windows poNoConsole
AProcess.Parameters.Add('/Application/iCal.app');
Если вашему приложению нужны параметры, вы можете передать open параметр --args, после чего все параметры будут переданы приложению:
AProcess.Parameters.Add('--args');
AProcess.Parameters.Add('argument1');
AProcess.Parameters.Add('argument2');
Запуск отдельной программы
Обычно программа, запускаемая вашим приложением, является дочерним процессом и уничтожается, когда ваше приложение уничтожается. Если вы хотите запустить автономную программу, которая продолжает работать [после завершения вашего приложения], вы можете использовать следующее:
var
Process: TProcess;
I: Integer;
begin
Process := TProcess.Create(nil);
try
Process.InheritHandles := False;
Process.Options := [];
Process.ShowWindow := swoShow;
// Копируем переменные среды по умолчанию, включая переменную DISPLAY, для работы приложения с графическим интерфейсом
for I := 1 to GetEnvironmentVariableCount do
Process.Environment.Add(GetEnvironmentString(I));
Process.Executable := '/usr/bin/gedit';
Process.Execute;
finally
Process.Free;
end;
end;
Пример "общения" с процессом aspell
Внутри исходного кода pasdoc вы можете найти два модуля, которые выполняют проверку орфографии, "общаясь" с запущенным процессом aspell через каналы:
- Модуль PasDoc_ProcessLineTalk.pas реализует класс TProcessLineTalk, потомок TProcess, который может быть легко использован для общения с любым процессом построчно.
- Модуль PasDoc_Aspell.pas реализует класс TAspellProcess, который выполняет проверку орфографии с использованием базового экземпляра TProcessLineTalk для выполнения aspell и связи с запущенным процессом aspell.
Оба модуля довольно независимы от остальных исходников pasdoc, поэтому они могут служить реальными примерами использования TProcess для запуска и связи по каналам с другой программой.
Замена операторов командной оболочки, таких как "| < >"
Иногда у вас есть желание выполнить более сложную команду, которая передает свои данные другой команде или файлу. Что-то вроде
ShellExecute('firstcommand.exe | secondcommand.exe');
или
ShellExecute('dir > output.txt');
Запуск таких команд с TProcess не будет работать. То есть:
// так работать не будет
Process.CommandLine := 'firstcommand.exe | secondcommand.exe';
Process.Execute;
Почему использование специальных операторов для перенаправления вывода не работает
TProcess - это не оболочка, а процесс. Это не два процесса, а всего один. Можно перенаправить вывод так, как вы этого пожелаете. См. следующий раздел.
Как перенаправить вывод с помощью TProcess
Вы можете перенаправлять вывод команды другой команде, используя экземпляр TProcess для каждой команды.
Вот пример, который объясняет, как перенаправить вывод одного процесса другому. Чтобы перенаправить вывод процесса в файл/поток, см. пример чтение больших объемов вывода.
Вы можете не только перенаправлять «обычный» вывод (также известный как stdout), но также можете перенаправить вывод ошибок (stderr), если вы укажете опцию poStderrToOutPut, как видно в настройках для второго процесса.
program Project1;
uses
Classes, sysutils, process;
var
FirstProcess,
SecondProcess: TProcess;
Buffer: array[0..127] of char;
ReadCount: Integer;
ReadSize: Integer;
begin
FirstProcess := TProcess.Create(nil);
SecondProcess := TProcess.Create(nil);
FirstProcess.Options := [poUsePipes];
FirstProcess.Executable := 'pwd';
SecondProcess.Options := [poUsePipes,poStderrToOutPut];
SecondProcess.Executable := 'grep';
SecondProcess.Parameters.Add(DirectorySeparator+ ' -');
// это было бы аналогично "pwd | grep / -"
FirstProcess.Execute;
SecondProcess.Execute;
while FirstProcess.Running or (FirstProcess.Output.NumBytesAvailable > 0) do
begin
if FirstProcess.Output.NumBytesAvailable > 0 then
begin
// убеждаемся, что мы не читаем больше данных, чем мы выделили
// для этого места в буфере
ReadSize := FirstProcess.Output.NumBytesAvailable;
if ReadSize > SizeOf(Buffer) then
ReadSize := SizeOf(Buffer);
// теперь считываем вывод в буфер
ReadCount := FirstProcess.Output.Read(Buffer[0], ReadSize);
// и пишем буфер во второй процесс
SecondProcess.Input.Write(Buffer[0], ReadCount);
// если SecondProcess записывает много данных в свой Output, то
// мы должны прочитать эти данные здесь, чтобы предотвратить блокировку
// см. предыдущий пример «Чтение больших объемов вывода»
end;
end;
// Закрываем вход на SecondProcess,
// поэтому он заканчивает обработку своих данных
SecondProcess.CloseInput;
// и дожидаемся его завершения
// будьте осторожны с тем, какую команду вы запускаете, потому что она может не завершиться,
// когда ее вход закроется, следующая строка может зациклиться навечно
while SecondProcess.Running do
Sleep(1);
// вот и все! остальная часть программы просто в качестве примера
// это немного «полезно»
// мы будем повторно использовать буфер для вывода SecondProcess'а
// вывод в *эту* программу stdout
WriteLn('Grep output Start:');
ReadSize := SecondProcess.Output.NumBytesAvailable;
if ReadSize > SizeOf(Buffer) then
ReadSize := SizeOf(Buffer);
if ReadSize > 0 then
begin
ReadCount := SecondProcess.Output.Read(Buffer, ReadSize);
WriteLn(Copy(Buffer,0, ReadCount));
end
else
WriteLn('grep did not find what we searched for. ', SecondProcess.ExitStatus);
WriteLn('Grep output Finish:');
// освобождаем объекты нашего процесса
FirstProcess.Free;
SecondProcess.Free;
end.
Вот и все. Теперь вы можете перенаправлять вывод из одной программы в другую.
Заметки
Этот пример может показаться излишним, поскольку можно запускать «сложные» команды, используя оболочку с TProcess, например:
Process.Commandline := 'sh -c "pwd | grep / -"';
Но наш пример более кроссплатформенный, так как он не нуждается в модификации для работы в Windows или Linux и т.д. "sh" может существовать или не существовать на вашей платформе и обычно доступен только на платформах *nix. Кроме того, у нас больше гибкости в нашем примере, так как вы можете читать и записывать из/в ввода, вывода и stderr каждого процесса в отдельности, что может быть очень выгодно для вашего проекта.
Redirecting input and output and running under root
A common problem on Unixes (OSX) and Linux is that you want to execute some program under the root account (or, more generally, another user account). An example would be running the ping command.
If you can use sudo for this, you could adapt the following example adapted from one posted by andyman on the forum ([1]). This sample runs ls
on the /root
directory, but can of course be adapted.
A better way to do this is to use the policykit package, which should be available on all recent Linuxes. See the forum thread for details.
Large parts of this code are similar to the earlier example, but it also shows how to redirect stdout and stderr of the process being called separately to stdout and stderr of our own code.
program rootls;
{ Demonstrates using TProcess, redirecting stdout/stderr to our stdout/stderr,
calling sudo on Linux/OSX, 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.
Other thoughts: It would no doubt be advisable to see if sudo actually prompts for a password. This can be checked consistently by setting the environment variable SUDO_PROMPT to something we watch for while reading the stdout of TProcess avoiding the problem of the prompt being different for different locales. Setting an environment variable causes the default values to be cleared(inherited from our process) so we have to copy the environment from our program if needed.
Using fdisk with sudo on Linux
The following example shows how to run fdisk on a Linux machine using the sudo command to get root permissions. Note: this is an example only, and does not cater for large output.
program getpartitioninfo;
{Originally contributed by Lazarus forums wjackon153. Please contact him for questions, remarks etc.
Modified from Lazarus snippet to FPC program for ease of understanding/conciseness by BigChimp}
Uses
Classes, SysUtils, FileUtil, Process;
var
hprocess: TProcess;
sPass: String;
OutputLines: TStringList;
begin
sPass := 'yoursudopasswordhere'; // You need to change this to your own sudo password
OutputLines:=TStringList.Create; //... a try...finally block would be nice to make sure
// OutputLines is freed... Same for hProcess.
// The following example will open fdisk in the background and give us partition information
// Since fdisk requires elevated priviledges we need to
// pass our password as a parameter to sudo using the -S
// option, so it will will wait till our program sends our password to the sudo application
hProcess := TProcess.Create(nil);
// On Linux/Unix/OSX, we need specify full path to our executable:
hProcess.Executable := '/bin/sh';
// Now we add all the parameters on the command line:
hprocess.Parameters.Add('-c');
// Here we pipe the password to the sudo command which then executes fdisk -l:
hprocess.Parameters.add('echo ' + sPass + ' | sudo -S fdisk -l');
// Run asynchronously (wait for process to exit) and use pipes so we can read the output pipe
hProcess.Options := hProcess.Options + [poWaitOnExit, poUsePipes];
// Now run:
hProcess.Execute;
// hProcess should have now run the external executable (because we use poWaitOnExit).
// Now you can process the process output (standard output and standard error), eg:
OutputLines.Add('stdout:');
OutputLines.LoadFromStream(hprocess.Output);
OutputLines.Add('stderr:');
OutputLines.LoadFromStream(hProcess.Stderr);
// Show output on screen:
writeln(OutputLines.Text);
// Clean up to avoid memory leaks:
hProcess.Free;
OutputLines.Free;
//Below are some examples as you see we can pass illegal characters just as if done from terminal
//Even though you have read elsewhere that you can not I assure with this method you can :)
//hprocess.Parameters.Add('ping -c 1 www.google.com');
//hprocess.Parameters.Add('ifconfig wlan0 | grep ' + QuotedStr('inet addr:') + ' | cut -d: -f2');
//Using QuotedStr() is not a requirement though it makes for cleaner code;
//you can use double quote and have the same effect.
//hprocess.Parameters.Add('glxinfo | grep direct');
// This method can also be used for installing applications from your repository:
//hprocess.Parameters.add('echo ' + sPass + ' | sudo -S apt-get install -y pkg-name');
end.
Parameters which contain spaces (Replacing Shell Quotes)
In the Linux shell it is possible to write quoted arguments like this:
gdb --batch --eval-command="info symbol 0x0000DDDD" myprogram
And GDB will receive 3 arguments (in addition to the first argument which is the full path to the executable):
- --batch
- --eval-command=info symbol 0x0000DDDD
- the full path to myprogram
TProcess can also pass parameters containing spaces, but it uses a different quoting style. Instead of only quoting part of the parameter, quote all of it. Like this:
TProcess.CommandLine := '/usr/bin/gdb --batch "--eval-command=info symbol 0x0000DDDD" /home/me/myprogram';
And also remember to only pass full paths.
See also this discussion about it: http://bugs.freepascal.org/view.php?id=14446
Альтернативные решения с помощью LCLIntf
Иногда вам нет необходимости явно вызывать внешнюю программу для получения необходимой функциональности. Вместо запуска приложения и передачи в него документа, просто выполните запрос через операционную систему открыть документ и использовать для этого приложение по-умолчанию, ассоциированное с данным типом файлов. Ниже представлены некоторые примеры.
Открыть документ в приложении по умолчанию
В некоторых ситуациях вам необходимо открыть документ/файл в приложении, используемом по умолчанию для данного типа документов. Это зависит от того, в какой операционной системе выполняется приложение. Lazarus предоставляет платформонезависимую процедуру OpenDocument, которая будет обрабатывать такой запрос. Ваше приложение будет продолжать работать, не дожидаясь закрытия документа в вызываемом приложении.
uses LCLIntf;
...
OpenDocument('manual.pdf');
...
Открыть страницу в браузере по умолчанию
Просто передайте необходимый URL; при этом указание http:// при определенных обстоятельствах является необязательным. Передача имени файла дает такой же результат, как и использование OpenDocument()
uses LCLIntf;
...
OpenURL('www.lazarus.freepascal.org/');
См. также:
Кроме того, вы можете использовать TProcess как в данном примере:
uses Process;
procedure OpenWebPage(URL: string);
// Apparently you need to pass your URL inside ", like "www.lazarus.freepascal.org"
var
Browser, Params: string;
begin
FindDefaultBrowser(Browser, Params);
with TProcess.Create(nil) do
try
Executable := Browser;
Params:=Format(Params, [URL]);
Params:=copy(Params,2,length(Params)-2); // remove "", the new version of TProcess.Parameters does that itself
Parameters.Add(Params);
Options := [poNoConsole];
Execute;
finally
Free;
end;
end;