Манипулирование процессами:
Запускает программу напрямую | Выполняет команду оболочки | |
---|---|---|
Создание процесса на низком уровне | spawnProcess | spawnShell |
Автоматическое перенаправление ввода/вывода с использованием каналов pipe | pipeProcess | pipeShell |
Выполнение и ожидание завершения, сбор вывода | execute | executeShell |
Другая функциональность:
Исходный код: std/process.d
Командная строка: Существует четыре перегрузки этой функции. Первые две принимают массив строк args, который должен нулевым элементом содержать имя программы, а также любые аргументы командной строки в последующих элементах. Третья и четвертая версии включены для удобства, и их можно использовать в случае отсутствия аргументов. Они принимают одну строку program, которая определяет имя программы.
Если в args[0] или в program не указан каталог, spawnProcess будет искать программу платформо-зависимым способом. В системах POSIX он будет искать исполняемый файл в каталогах, перечисленных в переменной среды PATH, в том же порядке, в котором они перечислены. В Windows он будет искать исполняемый файл в следующей последовательности:// Запуск исполняемого файла под названием «prog», // расположенного в текущем рабочем каталоге: auto pid = spawnProcess("./prog"); scope(exit) wait(pid); // Мы можем делать что-то ещё во время работы программы. // Защита области видимости scope гарантирует, // что ожидание процесса произойдёт в конце этой области видимости. ... // Запуск DMD с файлом «myprog.d», с указанием нескольких опций компилятора: auto dmdPid = spawnProcess(["dmd", "-O", "-release", "-inline", "myprog.d" ]); if (wait(dmdPid) != 0) writeln("Compilation failed!");
Системные переменные: По умолчанию дочерний процесс наследует среду родительского процесса вместе с любыми дополнительными переменными, указанными в параметре env. Если одна и та же переменная существует как в родительской среде, так и в env, последняя имеет приоритет.
Если в параметре config установлен флаг Config.newEnv, дочерний процесс не будет наследовать родительскую среду. Всё его окружение будет определяться с помощью env.wait(spawnProcess("myapp", ["foo" : "bar"], Config.newEnv));
Стандартные потоки: Необязательные аргументы stdin, stdout и stderr могут использоваться для назначения дочернему процессу произвольных объектов std.stdio.File в качестве стандартных потоков, соответственно, ввода, вывода и ошибок. Первый должен быть открыт для чтения, а остальные два должны быть открыты для записи. По умолчанию дочерний процесс наследует стандартные потоки своего родителя.
// Запуск DMD с файлом myprog.d, с записью всех сообщений об ошибках // в файле с именем errors.log. auto logFile = File("errors.log", "w"); auto pid = spawnProcess(["dmd", "myprog.d"], std.stdio.stdin, std.stdio.stdout, logFile); if (wait(pid) != 0) writeln("Compilation failed. See errors.log for details.");Обратите внимание: если вы передадите объект File, который не является одним из стандартных потоков ввода/вывода/ошибок родительского процесса, по умолчанию этот поток будет закрыт в родительском процессе при возвращении из этой функции. Подробнее об отключении такого поведения смотрите в документации по флагам Config. Остерегайтесь проблем с буферизацией при передаче объектов File в функцию spawnProcess. Дочерний процесс наследует низкоуровневое исходное смещение чтения/записи, связанное с нижележащим файловым дескриптором, но он не будет знать о каких-либо буферизованных данных. В случаях, когда это имеет значение (например, когда файл должен быть выровнен перед передачей дочернему процессу), может быть хорошей идеей использовать небуферизованные потоки, или, по крайней мере, выполнить flush для всех соответствующих буферов.
char[][] args | Массив, который содержит в нулевом элементе имя программы, и любые аргументы командной строки в последующих элементах. |
File stdin | Стандартный поток ввода дочернего процесса. Это может быть любой объект std.stdio.File, который открыт для чтения. По умолчанию дочерний процесс наследует поток ввода родителя. |
File stdout | Стандартный поток вывода дочернего процесса. Это может быть любой объект std.stdio.File, который открыт для записи. По умолчанию дочерний процесс наследует поток вывода родителя. |
File stderr | Стандартный поток ошибок дочернего процесса. Это может быть любой объект std.stdio.File, который открыт для записи. По умолчанию дочерний процесс наследует поток ошибок родителя. |
string[string] env | Дополнительные переменные среды для дочернего процесса. |
Config config | Флаги, которые управляют созданием процесса. Смотрите Config для подробностей о доступных флагах. |
char[] workDir | Рабочий каталог для нового процесса. По умолчанию дочерний процесс наследует рабочий каталог родительского процесса. |
// Обработать файл с именем "my file.txt" командой/программой "foo", и // перенаправить её вывод в файл foo.log. auto pid = spawnShell(`foo "my file.txt" > foo.log`); wait(pid);
Пример:
auto logFile = File("myapp_error.log", "w"); // Запуск программы с подавлением консольного окна (только для Windows) и // перенаправлением потока ошибок в logFile. // logFile остаётся открытым в родительском процессе. auto pid = spawnProcess("myapp", stdin, stdout, logFile, Config.retainStderr | Config.suppressConsole); scope(exit) { auto exitCode = wait(pid); logFile.writeln("myapp exited with code ", exitCode); logFile.close(); }
Особенности в POSIX: Если процесс завершается по сигналу, эта функция вернёт отрицательное число, абсолютное значение которого является номером сигнала. Поскольку POSIX ограничивает нормальные коды статуса выхода до диапазона 0-255, отрицательное возвращаемое значение всегда указывает на завершение по сигналу. Коды сигналов определены в модуле core.sys.posix.signal (который соответствует заголовочному файлу POSIX signal.h).
Пример: Смотрите документацию по spawnProcess.
Пример:
auto pid = spawnProcess("dmd myapp.d"); scope(exit) wait(pid); ... auto dmd = tryWait(pid); if (dmd.terminated) { if (dmd.status == 0) writeln("Компиляция прошла успешно!"); else writeln("Ошибка компиляции"); } else writeln("Всё ещё компилируется..."); ...Обратите внимание, что в этом примере первый вызов wait не будет иметь эффекта, если процесс уже завершён к моменту вызова tryWait. Однако, в противном случае оператор scope гарантирует, что мы всегда будем ждать завершения процесса, если он не завершится к моменту выхода из области видимости.
Особенности в Windows: Процесс будет принудительно и резко завершён. Если задан codeOrSignal, это должно быть неотрицательное число, которое будет использоваться в качестве кода выхода процесса. Если нет, процесс завершится с кодом 1. Не используйте codeOrSignal = 259, так как это специальное значение (так называемое STILL_ACTIVE), используемое в Windows для сигнала о том, что процесс фактически ещё не завершён.
auto pid = spawnProcess("some_app"); kill(pid, 10); assert (wait(pid) == 10);
Особенности в POSIX: процессу будет отправлен сигнал, значение которого задается параметром codeOrSignal. В зависимости от отправленного сигнала процесс может завершиться, а может и не завершиться. Символьные константы для различных POSIX-сигналов определены в файле core.sys.posix.signal, который соответствует заголовочному файлу POSIX signal.h. Если параметр codeOrSignal опущен, будет отправлен сигнал SIGTERM. (Это соответствует поведению shell-команды kill.)
import core.sys.posix.signal : SIGKILL; auto pid = spawnProcess("some_app"); kill(pid, SIGKILL); assert (wait(pid) == -SIGKILL); // Отрицательное возвращаемое значение для POSIX!
auto p = pipe(); p.writeEnd.writeln("Hello World"); p.writeEnd.flush(); assert (p.readEnd.readln().chomp() == "Hello World");Каналы могут использоваться, например, для межпроцессного взаимодействия путем порождения нового процесса и передачи одного конца канала дочернему процессу, в то время как родитель использует другой конец. (См. также pipeProcess и pipeShell, позволяющие сделать то же самое проще.)
// cURL используйтся для загрузки главной страницы dlang.org, её вывод // перенаправляется в grep, чтобы извлечь список ссылок на ZIP-файлы, // и полученный список пишется в файл "D downloads.txt": auto p = pipe(); auto outFile = File("D downloads.txt", "w"); auto cpid = spawnProcess(["curl", "http://dlang.org/download.html"], std.stdio.stdin, p.writeEnd); scope(exit) wait(cpid); auto gpid = spawnProcess(["grep", "-o", `http://\S*\.zip`], p.readEnd, outFile); scope(exit) wait(gpid);
char[][] args | Массив, содержащий имя программы в нулевом элементе и все остальные аргументы командной строки в последующих элементах. (Подробности смотрите в документации по spawnProcess.) |
char[] program | Имя программы, без аргументов командной строки. (Подробности смотрите в документации по spawnProcess.) |
char[] command | Команда оболочки, которая дословно передаётся командному интерпретатору. (Подробности смотрите в документации по spawnShell.) |
Redirect redirect | Флаги, которые определяют, какие потоки перенаправляются, и как. См. документацию по Redirect для информации по доступным флагам. |
string[string] env | Дополнительные переменные окружения для дочернего процесса. (Подробности смотрите в документации по spawnProcess.) |
Config config | Флаги, которые управляют созданием процесса. См. Config для обзора доступных флагов, и обратите внимание, что флаги retainStd... не влияют на эту функцию. |
char[] workDir | Рабочий каталог для нового процесса. По умолчанию дочерний процесс наследует рабочий каталог родительского процесса. |
string shellPath | Путь к оболочке, используемой для запуска указанной программы program. По умолчанию это nativeShell. |
Пример:
// my_application пишет в stdout и может писать в stderr auto pipes = pipeProcess("my_application", Redirect.stdout | Redirect.stderr); scope(exit) wait(pipes.pid); // Сохранение строк вывода. string[] output; foreach (line; pipes.stdout.byLine) output ~= line.idup; // Сохранение строк ошибок. string[] errors; foreach (line; pipes.stderr.byLine) errors ~= line.idup; // sendmail будет читать из stdin pipes = pipeProcess(["/usr/bin/sendmail", "-t"], Redirect.stdin); pipes.stdin.writeln("To: you"); pipes.stdin.writeln("From: me"); pipes.stdin.writeln("Subject: dlang"); pipes.stdin.writeln(""); pipes.stdin.writeln(message); // единичная точка сообщает sendmail, что мы закончили pipes.stdin.writeln("."); // но в этот момент sendmail, возможно, не увидит её, нам нужно выполнить flush pipes.stdin.flush(); // sendmail выходит из системы по ".", но вы должны закрыть файл: pipes.stdin.close(); // иначе это ожидание будет вечным wait(pipes.pid);
auto dmd = execute(["dmd", "myapp.d"]); if (dmd.status != 0) writeln("Ошибка компиляции:\n", dmd.output); auto ls = executeShell("ls -l"); if (ls.status != 0) writeln("Не удалось получить список файлов"); else writeln(ls.output);Параметры args/program/command, env и config перенаправляются непосредственно в лежащие в основе порождающие функции, и подробно описаны в документации по этим функциям.
char[][] args | Массив, содержащий имя программы в нулевом элементе и все остальные аргументы командной строки в последующих элементах. (Подробности смотрите в документации по spawnProcess.) |
char[] program | Имя программы, без аргументов командной строки. (Подробности смотрите в документации по spawnProcess.) |
char[] command | Команда оболочки, которая дословно передаётся командному интерпретатору. (Подробности смотрите в документации по spawnShell.) |
string[string] env | Дополнительные переменные окружения для дочернего процесса. (Подробности смотрите в документации по spawnProcess.) |
Config config | Флаги, которые управляют созданием процесса. См. Config для обзора доступных флагов, и обратите внимание, что флаги retainStd... не влияют на эту функцию. |
size_t maxOutput | Максимальное количество выводимых байт, которое должно быть перехвачено. |
char[] workDir | Рабочий каталог для нового процесса. По умолчанию дочерний процесс наследует рабочий каталог родительского процесса. |
string shellPath | Путь к оболочке, используемой для запуска указанной программы program. По умолчанию это nativeShell. |
Особенности в POSIX: Если процесс завершается по сигналу, поле возвращаемого значения status будет содержать отрицательное число, абсолютное значение которого является номером сигнала. (Для подробностей смотрите документацию по функции wait.)
Пример:
writefln("ID текущего процесса: %d", thisProcessID);
Пример:
writefln("ID текущего потока: %s", thisThreadID);
string url = "http://dlang.org/"; executeShell(escapeShellCommand("wget", url, "-O", "dlang-index.html"));
executeShell( escapeShellCommand("curl", "http://dlang.org/download.html") ~ "|" ~ escapeShellCommand("grep", "-o", `http://\S*\.zip`) ~ ">" ~ escapeShellFileName("D download links.txt"));
auto path = environment["PATH"];
auto sh = environment.get("SHELL", "/bin/sh");Эта функция также полезна для проверки существования переменной окружения.
auto myVar = environment.get("MYVAR"); if (myVar is null) { // Переменная окружения не существует. // Обратите внимание, что мы должны использовать 'is' для сравнения, // так как myVar == null также вернёт истину, // если переменная существует, но является пустой. }
environment["foo"] = "bar";
Особенности в Windows: В то время как имена переменных среды Windows нечувствительны к регистру, для встроенных ассоциативных массивов в D это не так. Эта функция сохранит все имена переменных в верхнем регистре (например, PATH).
Особенности в Windows: Эти функции поддерживаются только на платформах POSIX, так как операционные системы Windows не предоставляют возможность перезаписывать текущий образ процесса другим процессом. В однопоточных программах возможно получить похожий на execv* эффект, используя spawnProcess и завершив текущий процесс после возвращения дочернего процесса. Например:
auto commandLine = [ "program", "arg1", "arg2" ]; version (Posix) { execv(commandLine[0], commandLine); throw new Exception("Не удалось выполнить программу"); } else version (Windows) { import core.stdc.stdlib : _exit; _exit(wait(spawnProcess(commandLine))); }Это, однако, НЕ эквивалентно POSIX-функции execv*. Во-первых, стартуемая программа запускается как отдельный процесс, со всеми вытекающими отсюда последствиями. Во-вторых, в многопоточной программе другие потоки будут продолжать работать, пока текущий поток ожидает завершения дочернего процесса. Иногда лучшим вариантом может быть завершение текущей программы сразу после порождения дочернего процесса. Это поведение, проявляемое функциями_exec в библиотеке времени выполнения Microsoft C, и именно так работали в D устаревшие в данный момент функции execv* для Windows. Пример:
auto commandLine = [ "program", "arg1", "arg2" ]; version (Posix) { execv(commandLine[0], commandLine); throw new Exception("Failed to execute program"); } else version (Windows) { spawnProcess(commandLine); import core.stdc.stdlib : _exit; _exit(0); }