如何防止管道流入脚本中的子 shell

如何防止管道流入脚本中的子 shell

我将管道传输到 PHP 脚本中(请参阅下面的人为示例)。遗憾的是,管道无意中流入了脚本中的 shell 命令,因此 nano 无法运行,因为它在 STDIN 上阻塞。

我希望 shell 命令的运行与通过管道传输到主脚本的 STDIN 完全无关。因此 PHP 脚本应该以某种方式“吃掉”STDIN,这样它就不会到达子 shell。我该如何解决?

请注意exec()system()passthru()都给出相同的结果。

$ echo -e "World\nEverybody" | php script.php
Hello World
Received SIGHUP or SIGTERM
Hello Everybody
Received SIGHUP or SIGTERM

脚本.php:

<?php

foreach(file("php://stdin") as $name) {
  echo "Hello $name";
  passthru("nano");
}

?>

环境:

  • PHP 7.1.14 / PHP 5.6.30
  • GNU bash,版本 3.2.57

答案1

是的,当您执行以下操作时,进程会继承其父进程的文件描述符:

php -r 'passthru("nano");'

php将继承 shell 的 stdin(如果在交互式 shell 的提示下调用,则为 tty 设备),并且也会继承它(而 stdout 是用于检索输出并传递它的nano管道,似乎对它,并非所有编辑器都会这样做,您可能想在此处使用)。phpnanonanosystem()

在:

something | php -r 'passthru("nano");'

现在,您正在php用它的 stdin 调用一个管道,something另一端有 's stdout 。并将nano继承它。

如果您希望php的 stdin 成为管道,而nanostdin 无论 shell 的 stdin 是什么,您需要以某种方式将该资源传递给php,并使其php(或由 运行的 shell passthru)使其成为 的标准输入nano。例如可以这样做:

{ something 3<&- | php -r 'passthru("nano <&3 3<&-");'; } 3<&0

我们使 fd 0 (stdin) 上的资源在命令组 ( {...;}) 中的 fd 3 上也可用,为something不需要它的 关闭它 ( 3<&-),并告诉 php 运行的 shellpassthru从该 fd 3 恢复 stdin 。

例子:

$ php -r 'passthru("ls -l /proc/self/fd");'
total 0
lrwx------ 1 stephane stephane 64 Mar 19 15:12 0 -> /dev/pts/38
l-wx------ 1 stephane stephane 64 Mar 19 15:12 1 -> pipe:[22538485]
lrwx------ 1 stephane stephane 64 Mar 19 15:12 2 -> /dev/pts/38

fd 0 是用于终端交互的 tty 设备。

$ echo hello | php -r 'passthru("ls -l /proc/self/fd");'
total 0
lr-x------ 1 stephane stephane 64 Mar 19 15:12 0 -> pipe:[22539326]
l-wx------ 1 stephane stephane 64 Mar 19 15:12 1 -> pipe:[22530020]
lrwx------ 1 stephane stephane 64 Mar 19 15:12 2 -> /dev/pts/38

现在ls的 stdin 是一个管道(echo正在馈送)。

$ { echo hello 3<&- | php -r 'passthru("ls -l /proc/\$PPID/fd /proc/self/fd <&3 3<&-");';} 3<&0
/proc/9202/fd:
total 0
lr-x------ 1 stephane stephane 64 Mar 19 15:17 0 -> pipe:[22544619]
lrwx------ 1 stephane stephane 64 Mar 19 15:17 1 -> /dev/pts/38
lrwx------ 1 stephane stephane 64 Mar 19 15:17 2 -> /dev/pts/38
lrwx------ 1 stephane stephane 64 Mar 19 15:17 3 -> /dev/pts/38
lr-x------ 1 stephane stephane 64 Mar 19 15:17 4 -> pipe:[22544623]

/proc/self/fd:
total 0
lrwx------ 1 stephane stephane 64 Mar 19 15:17 0 -> /dev/pts/38
l-wx------ 1 stephane stephane 64 Mar 19 15:17 1 -> pipe:[22544623]
lrwx------ 1 stephane stephane 64 Mar 19 15:17 2 -> /dev/pts/38

ls的 stdin 已再次成为 tty 设备,而其父级 (php) 仍然具有 stdin 上的管道(另请参阅 fd 3 上的 tty 和 fd 4 上的另一个管道,可能是它正在读取lswith 的输出的管道)。

所以在这里,您需要将 php 脚本更改为:

<?php
foreach(file("php://stdin") as $name) {
  echo "Hello $name";
  passthru("nano <&3 3<&-");
}
?>

并将其称为:

{ printf '%s\n' World Everybody | php script.php; } 3<&0

将两个资源(来自的管道printf和原始标准输入)传递到php.

如果您希望该php脚本始终从终端内调用,并且nano应该始终与终端交互(但话又说回来,请注意,这php使得它标准输出 不是终端),您可以将其更改为:

<?php
foreach(file("php://stdin") as $name) {
  echo "Hello $name";
  passthru("nano < /dev/tty");
}
?>

我们将 的 stdin 硬编码nano为控制终端。

相关内容