zsh: a substituição de comando não herda stdin de seu pai

3

Considere o seguinte comando:

seq 5 | grep $(tail -n1) <(seq 9)

Ao executá-lo em zsh 1 :

tail: error reading 'standard input': Input/output error

Agora executando o mesmo em bash , ele gera:

5

OK. Conforme explicado nos comentários, a substituição de comando $(tail -n1) herda stdin de seu pai. Mas por que isso não acontece com zsh ?
Isso é uma coisa zsh -only ou é algo que outras shells também fazem? Onde está documentado?

Agora, se eu executar o mesmo comando via zsh -c :

zsh -c 'seq 5 | grep $(tail -n1) <(seq 9)'

em vez de imprimir a mesma mensagem de erro, ele para depois de tail -n1 e aguarda a entrada do usuário, por isso, se eu digitar

19
2
4

depois pressione Ctrl + D , imprime

4

O que está acontecendo aqui?

1: isso é com zsh 5.3.1 on archlinux , se for importante.

    
por don_crissti 17.01.2017 / 18:17

1 resposta

5

Você perceberá que em bash / ksh

echo foo | echo "$(cat)"

exibe foo , mas

<<< foo echo "$(cat)"

Não.

No primeiro caso, o $(cat) é expandido no processo filho que executará echo depois que seu stdin for redirecionado do canal.

No segundo caso, o $(cat) é expandido antes do redirecionamento.

pipes e redirecionamentos são coisas diferentes. pipes envolvem algum redirecionamento , mas também iniciam comandos em paralelo. Isso acontece cedo, antes dos redirecionamentos dentro de cada componente do pipe.

Em zsh

$ sleep 1 | ps -jfH $(ps -fH >&2)
UID        PID  PPID  C STIME TTY          TIME CMD
chazelas  2495  2494  0 20:59 pts/1    00:00:00 /bin/zsh
chazelas 31201  2495  0 21:20 pts/1    00:00:00   sleep 1
chazelas 31202  2495  0 21:20 pts/1    00:00:00   ps -fH
UID        PID  PPID  PGID   SID  C STIME TTY          TIME CMD
chazelas  2495  2494  2495  2495  0 20:59 pts/1    00:00:00 /bin/zsh
chazelas 31201  2495 31201  2495  0 21:20 pts/1    00:00:00   sleep 1
chazelas 31203  2495 31201  2495  0 21:20 pts/1    00:00:00   ps -jfH

Você notará que, desta vez, a substituição do comando é expandida pelo shell pai.

Uma coisa a ter em mente é que, em zsh , os pipes são tratados um pouco mais como redirecionamentos em particular quando se trata da opção mult_ios (ativada por padrão).

Quando você faz:

echo foo > file | tr o e

foo vai para os dois file e tr .

Em:

uname | cat < /etc/issue

cat é alimentado com a saída de uname e o conteúdo de /etc/issue . Portanto, em zsh , o redirecionamento de < / > e do pipe deve acontecer no mesmo estágio. De preferência após as expansões.

Em qualquer caso, você sempre pode fazer:

echo foo | { echo "$(cat)"; }

em zsh e bash / ksh , como sempre é possível fazer:

{ echo "$(cat)"; } <<< foo

em bash e zsh .

Quanto à causa do:

tail: error reading 'standard input': Input/output error

erro. Em shells interativos, uma vez que a substituição de comandos é feita no pai, isso não é feito no grupo de processos em primeiro plano do terminal.

tail será executado no grupo de processos do shell pai. Se esse shell for o líder da sessão, ele será um grupo de processos órfãos , portanto quando tail tentar ler o dispositivo tty, ele falhará com a EIO.

Se zsh não foi o líder da sessão. Por exemplo, se você iniciou zsh de outro shell, o grupo de processos receberia um SIGTTIN. O processo principal do shell iria ignorá-lo, mas tail acabaria sendo suspenso.

    
por 17.01.2017 / 22:30