防止管道进入脚本的子shell

时间:2018-03-19 14:40:45

标签: php shell pipe

我正在将线条划分为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

的script.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
  • GNU nano版本2.0.6

1 个答案:

答案 0 :(得分:1)

管道并没有真正流入子壳。事实上,没有任何东西流入。为了将nano的STDIN连接到终端,你将控制终端(总是/dev/tty)传输到nano,如下所示:

passthru("nano </dev/tty");

以下是您的后续问题的答案。 (非常好的问题IMO。我之前的回答实际上有些错误.STDIN会流入子进程。)

  

如果脚本只包含passthru(&#34; nano&#34;)并且你没有将任何内容传递给PHP,那么nano不需要&lt; / dev / tty。这是为什么?

Linux行为

实际上,子进程确实从父进程继承了STDIN,但由于缓冲,有时并不完全清楚。由于它们继承了相同的STDIN,当达到EOF时,它们会在达到EOF时执行任何操作(查看nano在这种情况下的作用,见下文)。

让我们把PHP从等式中拿出来,看看当我们打开或关闭缓冲时我们得到了什么。这里有一些C代码,它们将从STDIN,system()读取,并再次从STDIN读取:

#include <stdio.h>
#include <stdlib.h>

int main() {
    // setvbuf(stdin, NULL, _IONBF, 0 );
    char buffer[32];
    gets(buffer);
    printf("Hello %s\n", buffer);
    system("bash -c 'read FOO; echo This is bash, got $FOO'");
    gets(buffer);
    printf("Hello2 %s\n", buffer);
}

编译(忽略有关gets的警告)并运行:

$ cc -o script script.c
$ echo -e "Foo\nBar\nCar" | ./script
Hello Foo
This is bash, got
Hello2 Bar

bash没有得到任何结果。 gets之后的system神奇地得到了这个输入。现在取消注释第一行:

-    // setvbuf(stdin, NULL, _IONBF, 0 );
+    setvbuf(stdin, NULL, _IONBF, 0 );

我们得到:

$ cc -o script script.c
$ echo -e "Foo\nBar\nCar" | ./script
Hello Foo
This is bash, got Bar
Hello2 Car

这一次bash得到了第二个输入。 &#34;太长了;没有读过&#34;:我们确实有相同的STDIN。

`nano` internals

首先,你会发现nano的行为是相同的,即使你把PHP排除在等式之外:

$ echo foo | nano
Received SIGHUP or SIGTERM
理论上,{p> nano可以很好地检测出我们是否有终端,如果我们没有,则尝试打开/dev/tty(它只是一个常规终端) open致电)。事实上,如果您执行nanonano会执行此操作:

echo foo | nano -

scoop_stdin中的src/nano.c函数在版本2.9.4中处理此问题:http://git.savannah.gnu.org/cgit/nano.git/tree/src/nano.c?h=v2.9.4#n1122
版本2.7.4中的finish_stdin_pager函数:http://git.savannah.gnu.org/cgit/nano.git/tree/src/nano.c?h=v2.7.4#n1116

那么当nano获得EOF时会发生什么?键输入中的EOF如下处理: 版本2.7.4:get_key_buffer()直接调用handle_hupterm(0)
http://git.savannah.gnu.org/cgit/nano.git/tree/src/winio.c?h=v2.7.4#n207

版本2.9.4:die(_("Too many errors from stdin"));
http://git.savannah.gnu.org/cgit/nano.git/tree/src/winio.c?h=v2.9.4#n207

(我之间链接两者的原因是因为消息在某些时候发生了变化。)

我希望这会让事情变得更清楚。