В Python мы можем сделать что-то вроде этого:
pattern = "foo"searchfile = "/root/text"search = "bar"replace = "baz"
tempfile = tempfile.mkstemp()[1]
grepcmd = ["/bin/grep", "-E", pattern, searchfile]
sedcmd = ["/bin/sed", "-E", "s/{0}/{1}/g".format(search, replace)]
with open(tempfile, "w") as temphandle:
grep = subprocess.Popen(grepcmd, stdout=subprocess.PIPE)
sed = subprocess.Popen(sedcmd, stdin=grep.stdout, stdout=temphandle)
grep.stdout.close()
sed.communicate()
Вы можете видеть, что stdout
от grep
команда передается как stdin
к sed
команда, которая в свою очередь указывает дескриптор файла в качестве его stdout
, Это эквивалентно этой команде оболочки:
grep -E foo /root/text | sed -E 's/bar/baz/g' > /tmp/whatever
Я хотел бы повторить это поведение в PHP, но я не нахожу эквивалентную нативную функциональность. Я собирался создать эквивалентную команду оболочки в виде длинной строки и передать ее shell_exec()
но это грязно, особенно при правильном использовании escapeshellarg()
(Примечание: экранирование не требуется в приведенном выше коде Python.)
$pattern = "foo";
$searchfile = "/root/text";
$search = "bar";
$replace = "baz";
$tempfile = tempnam(sys_get_temp_dir(), 'asdf');
$grepcmd = "/bin/grep -E " . escapeshellarg($pattern) . " " . escapeshellarg($searchfile);
$sedcmd = "/bin/sed -E " . escapeshellarg("s/$search/$replace/g");
exec("$grepcmd | $sedcmd > $tempfile);
Я также могу запускать каждую команду отдельно, записывая вывод в переменную и передавая ее следующей команде, но это имеет очевидные недостатки.
Есть ли какой-нибудь родной способ сделать что-то подобное в PHP или мой текущий метод настолько хорош, насколько это возможно?
Ты можешь использовать proc_open
и связанные функции для этого.
$descriptorSpec = [
0 => ['pipe', 'r'], // stdin
1 => ['pipe', 'w'], // stdout
2 => ['pipe', 'w'], // stderr
];
$process = proc_open($cmd, $descriptorSpec, $pipes);
if (!is_resource($process)) {
throw new Exception('Unable to start shell process.');
}
$out = stream_get_contents($pipes[1]);
fclose($pipes[1]);
$error = stream_get_contents($pipes[2]);
fclose($pipes[2]);
$status = proc_close($process);
if ($status !== 0) {
throw new Exception('Command ' . $cmd . ' returned code ' . $status . '. Output: ' . $error);
}
Если вы хотите использовать выходные данные первой команды, передайте один из результирующих каналов дескриптору stdin другой команды:
$descriptorSpec = [
0 => $pipes[1], // stdin
1 => ['pipe', 'w'], // stdout
2 => ['pipe', 'w'], // stderr
];
Если вы хотите перенаправить один из дескрипторов в файл, просто укажите ресурс, из которого вы получаете fopen()
:
$res = fopen('file.log', 'w+');
$descriptorSpec = [
0 => ['pipe', 'r'], // stdin
1 => $res, // stdout
2 => $res, // stderr
];
Других решений пока нет …