PHP里调用系统命令

23 views
Skip to first unread message

AR

unread,
Dec 20, 2014, 11:47:14 AM12/20/14
to sh...@googlegroups.com
Hi, all

这几天在搬一个老项目,于是又折腾一把蛋疼的PHP。

原来PHP调用shell脚本完成工作,觉得太分离了,那一堆shell脚本以后肯定没人
能想起来那奇怪的写法是怎么回事,于是决定把功能移植进PHP里。

大致的需求是调用/bin/bash,需要拿到返回值,标准输入与标准输出,有时需要
login模式。

原来在Python里干过类似的事情,用subprocess还是挺惬意的。

谁知PHP...

自带的几个都不怎么行,proc_open勉强可以用,不过不支持login,还得自己绕。

于是用pcntl自己刻了一个[1],求吐槽。


[1]: https://gist.github.com/aleiphoenix/6d53a37474cc40b56c90



顺带把源代码稍上,不过感觉还是怪怪的,PHP里POSIX没有匿名管道,并发还得
自己刻

function subprocess($cmd, $login = FALSE, $cwd = './',
$shell = '/bin/bash', $pipespec = NULL) {

$pid = getmypid();

$inpipe = '/tmp/_php_inpipe' . $pid;
$outpipe = '/tmp/_php_outpipe' . $pid;
$errpipe = '/tmp/_php_errpipe' . $pid;
if ($pipespec) {
$inpipe = $pipespec['stdin'];
$outpipe = $pipespec['stdout'];
$errpipe = $pipespec['stderr'];
}

posix_mkfifo($inpipe, 0600);
posix_mkfifo($outpipe, 0600);
posix_mkfifo($errpipe, 0600);

$pid = pcntl_fork();

if ($pid) {
// parent
$ret = 0;

$out = fopen($outpipe, 'r');
$err = fopen($errpipe, 'r');

while (TRUE) {
$_ = pcntl_waitpid($pid, $status, WNOHANG);

if ($_ === 0) {
usleep(1000);
}
elseif (pcntl_wifexited($status)) {
$ret = pcntl_wexitstatus($status);
break;
}
else {
throw new RuntimeException();
};
}

$_stdout = '';
$_stderr = '';
while (!feof($out)) {
$_stdout .= fgets($out);
}


while (!feof($err)) {
$_stderr .= fgets($err);
}

fclose($out);
fclose($err);

unlink($inpipe);
unlink($outpipe);
unlink($errpipe);
$rv = array(
'ret' => $ret,
'stdout' => $_stdout,
'stderr' => $_stderr,
);
return $rv;
}
else {
if ($cwd) {
chdir($cwd);
}
$params = array('-c', "$cmd >${outpipe} 2>${errpipe}");
if ($login) {
array_unshift($params, '-l');
}
pcntl_exec($shell, $params);
}
}
signature.asc

feiandxs

unread,
Dec 20, 2014, 11:45:50 PM12/20/14
to sh...@googlegroups.com
虽然PHP是世界上最好的语言,但是SHELL里的东西干嘛到PHP里去写...

于2014年12月21日 0:47:01,AR写到:

依云

unread,
Dec 21, 2014, 1:23:09 AM12/21/14
to sh...@googlegroups.com
On Sun, Dec 21, 2014 at 12:47:01AM +0800, AR wrote:
> Hi, all
>
> 这几天在搬一个老项目,于是又折腾一把蛋疼的PHP。
>
> 原来PHP调用shell脚本完成工作,觉得太分离了,那一堆shell脚本以后肯定没人
> 能想起来那奇怪的写法是怎么回事,于是决定把功能移植进PHP里。
>
> 大致的需求是调用/bin/bash,需要拿到返回值,标准输入与标准输出,有时需要
> login模式。
>
> 原来在Python里干过类似的事情,用subprocess还是挺惬意的。
>
> 谁知PHP...
>
> 自带的几个都不怎么行,proc_open勉强可以用,不过不支持login,还得自己绕。
>
> 于是用pcntl自己刻了一个[1],求吐槽。
>
>
> [1]: https://gist.github.com/aleiphoenix/6d53a37474cc40b56c90

虽然不懂 PHP,不过还是好脆弱的样子。

fork 失败怎么办?$cmd 里有特殊字符怎么办?fifo 文件已存在怎么办?fifo 缓
冲区被填满了怎么办?还有你的 $inpipe 没用上。

--
Best regards,
lilydjwg

AR

unread,
Dec 22, 2014, 1:17:38 AM12/22/14
to sh...@googlegroups.com


On Dec 21, 2014 2:23 PM, "依云" <lily...@gmail.com> wrote:
> 虽然不懂 PHP,不过还是好脆弱的样子。
>
> fork 失败怎么办?$cmd 里有特殊字符怎么办?fifo 文件已存在怎么办?fifo 缓
> 冲区被填满了怎么办?还有你的 $inpipe 没用上。

还没补充地很完整。

特殊字符没考虑呢。之前用Python的subprocess也好奇这个怎么处理,PHP的pcntl下面是用execve的,应该也不用?FIFO缓冲问题,在上面用的时候已经很囧了。父进程如果没打开文件读,子进程会等在打开文件那里(这个没理解)。并发的问题,都怪PHP里没包匿名管道啦。最后发现好像只能到底层里搞。

其实一边在蛋疼地刻PHP时,就在想要不要索性用Python再刻一个(ry

依云

unread,
Dec 22, 2014, 8:18:52 AM12/22/14
to sh...@googlegroups.com
看你怎么玩。你直接 execve sh -c 的话,-c 后边那参数里的特殊字符要自己处
理,什么语言都一样。

FIFO 的特性就是这样子,另一端没准备好的时候,打都打不开。参见:
http://lilydjwg.is-programmer.com/2011/10/14/asynchronized-ipc-through-named-pipe.30205.html

> 其实一边在蛋疼地刻PHP时,就在想要不要索性用Python再刻一个(ry

Python 里已经有 subprocess 模块呀,不过有几个方便的函数只在 Python 3 提
供。

--
Best regards,
lilydjwg

Linux Vim Python 我的博客:
http://lilydjwg.is-programmer.com/
--
A: Because it obfuscates the reading.
Q: Why is top posting so bad?读端

AR

unread,
Dec 22, 2014, 7:11:12 PM12/22/14
to sh...@googlegroups.com
2014-12-22 21:18 GMT+08:00 依云 <lily...@gmail.com>:
> 看你怎么玩。你直接 execve sh -c 的话,-c 后边那参数里的特殊字符要自己处
> 理,什么语言都一样。
>
> FIFO 的特性就是这样子,另一端没准备好的时候,打都打不开。参见:
> http://lilydjwg.is-programmer.com/2011/10/14/asynchronized-ipc-through-named-pipe.30205.html

多谢,再多了解一下。

>> 其实一边在蛋疼地刻PHP时,就在想要不要索性用Python再刻一个(ry
>
> Python 里已经有 subprocess 模块呀,不过有几个方便的函数只在 Python 3 提
> 供。

某的意思是那个老的系统用Python再刻一个。现在如果没什么历史包袱都尽量用Python3了呢。XD


--
Silence is golden.
Reply all
Reply to author
Forward
0 new messages