使用PHP exec(或类似功能)时的重定向

时间:2016-10-11 17:53:54

标签: php io-redirection

在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命令又将文件句柄指定为{{1} }}。这相当于这个shell命令:

grep -E foo /root/text | sed -E 's/bar/baz/g' > /tmp/whatever

我想在PHP中复制此行为,但我找不到相同的本机功能。我一直在做的是将等效的shell命令构建为一个长字符串并将其传递给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中做类似的事情,或者我现在的方法是否会得到它的好处?

1 个答案:

答案 0 :(得分:1)

您可以使用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
];