Como evito que um pipe flua para um sub-shell em um script

1

Estou inserindo linhas em um script PHP (veja o exemplo inventado abaixo). Infelizmente, o pipe involuntariamente flui para o comando shell no script, portanto, o nano não é executado porque ele sufoca STDIN.

Eu quero que o comando shell seja executado completamente não relacionado ao STDIN canalizado para o script principal. Portanto, o script PHP deve, de certa forma, "comer" o STDIN, para que ele não atinja o sub-shell. Como faço para corrigir isso?

Observe que exec() , system() e passthru() todos fornecem o mesmo resultado.

$ 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");
}

?>

Ambiente:

  • PHP 7.1.14 / PHP 5.6.30
  • GNU bash, versão 3.2.57
por forthrin 19.03.2018 / 14:38

1 resposta

1

Sim, os processos herdam os descritores de arquivos de seus pais, quando você faz isso:

Em

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

php herdará o stdin do shell (um dispositivo tty se invocado no prompt de um shell interativo) e nano também o herdará (enquanto stdout é um canal usado por php para recuperar o shell saída de nano e passá-lo, nano parece estar feliz com isso, nem todos os editores, você pode querer usar system() aqui).

Em:

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

Você está chamando php com stdin agora um pipe com stdout de something na outra extremidade. E nano herdará isso.

Se você quiser que o stdin de php seja o canal, enquanto que nano stdin seja qual for o stdin do shell, você precisará passar esse recurso para php e ter php (ou o shell executado por passthru ) torná-lo o stdin de nano . Isso poderia ser feito com, por exemplo:

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

Quando disponibilizamos o recurso em fd 0 (stdin) no fd 3 dentro de um grupo de comando ( {...;} ), feche-o para something , que não precisa dele ( 3<&- ), e diga o shell é executado pelo passthru do php para restaurar o stdin daquele fd3.

Exemplo:

$ 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 é um dispositivo tty para interação terminal.

$ 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

Agora, o stdin de ls é um canal (o que a echo está alimentando).

$ { 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
O stdin de

ls foi feito novamente no dispositivo tty, enquanto seu pai (php) ainda tem o pipe em stdin (veja também o tty em fd 3 e outro pipe em fd 4, provavelmente o que está lendo a saída de ls com).

Então, aqui, você precisa alterar seu script php para:

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

E chame como:

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

Para passar os dois recursos (o canal de printf e o stdin original) para php .

Se você espera que o script php sempre seja chamado de dentro de um terminal e que nano sempre deve interagir com o terminal (mas, novamente, observe que php faz seu stdout não o terminal), você pode alterá-lo para:

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

Onde codificamos o stdin de nano para ser o terminal de controle.

    
por 19.03.2018 / 16:11

Tags