在 PHP 中,我使用 exec() 执行命令,如果成功则返回 URL;
$url = exec('report');
但是,我想检查 stderr,是否出现问题。我将如何读取流? 我想使用 php://stderr,但我不知道如何使用它。
如果您想执行命令,并同时获取
stderr
和 stdout
,而不是“合并”,则解决方案可能会使用 proc_open
,它可以对正在执行的命令提供很好的控制-- 包括一种管道方式 stdin
/stdout
/stderr
。
这是一个例子:让我们假设我们有一个 shell 脚本,位于
test.sh
中,它同时写入 stderr
和 stdout
:
#!/bin/bash
echo 'this is on stdout';
echo 'this is on stdout too';
echo 'this is on stderr' >&2;
echo 'this is on stderr too' >&2;
现在,让我们在
temp.php
中编写一些 PHP 代码——首先,我们初始化 i/o 描述符:
$descriptorspec = [
0 => ["pipe", "r"], // stdin
1 => ["pipe", "w"], // stdout
2 => ["pipe", "w"], // stderr
];
然后,在当前目录中使用这些描述符执行
test.sh
命令,并说明 i/o 应该是 from/to $pipes
:
$process = proc_open('./test.sh', $descriptorspec, $pipes, dirname(__FILE__), null);
我们现在可以从两个输出管道读取:
$stdout = stream_get_contents($pipes[1]);
fclose($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
fclose($pipes[2]);
并且,如果我们输出这两个变量的内容:
echo "stdout : \n";
var_dump($stdout);
echo "stderr :\n";
var_dump($stderr);
执行
temp.php
脚本时,我们得到以下输出:
$ php ./temp.php
stdout :
string(40) "this is on stdout
this is on stdout too
"
stderr :
string(40) "this is on stderr
this is on stderr too
"
一个可能有用的小功能:
function my_shell_exec($cmd, &$stdout=null, &$stderr=null) {
$proc = proc_open($cmd,[
1 => ['pipe','w'],
2 => ['pipe','w'],
],$pipes);
$stdout = stream_get_contents($pipes[1]);
fclose($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
fclose($pipes[2]);
return proc_close($proc);
}
返回退出代码,如果需要,STDOUT 和 STDERR 是参考参数。
使用
exec
执行此类操作的快捷方法是返回退出代码(命令的状态)
请注意,我正在尝试列出一个不存在的目录
/non-dir/
exec('ls /non-dir/', $out, $retval);
var_dump($retval);
输出
ls:无法访问“/non-dir/”:没有这样的文件或目录
整数(2)
通常在基于 UNIX 的系统中,大多数成功的状态代码是 ( 0 ),因此您可以检查
$retval
以了解命令的状态。
要消除列出无效路径的错误
ls: cannot access '/non-dir/': No such file or directory
,您可以将stderr重定向到null
exec('ls /non-dir/ 2>/dev/null', $out, $retval);
var_dump($retval);
这将输出:
int(2)
此外,如果您需要错误字符串在任何情况下使用它,您可以将 stderr 重定向到 stdout。
exec('ls /non-dir/ 2>&1', $out, $retval);
print_r($out);
var_dump($retval);
这将输出以下内容:
Array
(
[0] => ls: cannot access '/non-dir/': No such file or directory
)
int(2)
另一种取消合并 stdout/stderr 的方法。
$pp_name = "/tmp/pp_test";
@unlink($pp_name);
posix_mkfifo($pp_name, 0777);
$pp = fopen($pp_name, "r+");
stream_set_blocking($pp, FALSE);
exec("wget -O - http://www.youtube.com 2>$pp_name", $r_stdout);
$r_stderr = stream_get_contents($pp);
var_dump($r_stderr);
fclose($pp);
unlink($pp_name);
如果你想忽略 stdout 并只获取 stderr,你可以尝试这个:
exec("wget -O - http://www.youtube.com 2>&1 >/dev/null", $r_stderr);
exec("{$command} 2>&1"
,$output
,$exitCode
);
2>&1 将 stderr 重定向到 stdout 以实现一致的成功/失败行为。
$exitCode 确定 $command 完成状态。
$output 包含与 $exitCode 关联的所有输出。
虽然有点丑,但已经足够好了。将 stderr 放入临时文件并读取它。
$tmp = tempnam("/tmp", "ERR_");
exec('report 2> ' . escapeshellarg($tmp), $stdout, $retcode);
$stderr = file_get_contents($tmp);
unlink($tmp);
if ($retcode == 0)
{
// good
$url = $stdout[0];
} else {
// bad
error_log("FAIL: $stderr");
}