Por que existe um “/ dev / fd / 63” na saída de “echo 123 (cat)”?

5
$ echo 123 | cat 
123

está fazendo o que eu esperava, ambos os comandos rodam dentro do mesmo shell.

Mas quando eu os conecto com a expressão >( ... ) , que conecta a saída de um comando no shell a um segundo em um subshell, eu obtenho isto:

$ echo 123 >(cat)
123 /dev/fd/63

isso também é verdade com outros valores:

$ echo 101 >(cat)
101 /dev/fd/63

$ echo $BASHPID >(cat)
3252 /dev/fd/63

Eu achei que command1 >(command2) é o mesmo que command1 | command2 , mas em command1 >(command2) , cada comando está dentro de um shell diferente, portanto eles devem ter a mesma saída. Onde estou errado?

    
por sharkant 29.05.2017 / 10:04

4 respostas

8

A substituição do processo >(thing) será substituída por um nome de arquivo. Este nome de arquivo corresponde a um arquivo que está conectado à entrada padrão do thing dentro da substituição.

O seguinte seria um exemplo melhor de seu uso:

$ sort -o >(cat -n >/tmp/out) ~/.profile

Isso classificaria o arquivo ~/.profile e enviaria a saída para cat -n , que enumeraria as linhas e armazenaria o resultado em /tmp/out .

Então, para responder à sua pergunta: Você obtém essa saída porque echo obtém os dois argumentos 123 e /dev/fd/63 . /dev/fd/63 é o arquivo conectado à entrada padrão do processo cat na substituição do processo.

Modificando seu código de exemplo levemente:

$ echo 101 > >(cat)

Isso produziria apenas 101 na saída padrão (a saída de echo seria redirecionada para o arquivo que serve como entrada para cat e cat produziria o conteúdo desse arquivo na saída padrão ).

Observe também que no cmd1 | cmd2 pipeline, cmd2 pode não estar sendo executado no mesmo shell que cmd1 (dependendo da implementação do shell que você está usando). ksh93 funciona da maneira que você descreve (mesmo shell), enquanto bash cria um subshell para cmd2 (a menos que sua opção lastpipe shell esteja definida e o controle de trabalho não esteja ativo).

    
por 29.05.2017 / 10:15
5

Como o é o processo de substituição , ele faz o comando dentro do substituição aparecem como um nome de arquivo. Internamente, ele conecta os comandos através de um pipe, fornecendo o caminho /dev/fd/NN para o comando principal, para que ele possa abrir o descritor de arquivo já aberto para o pipe.

Não é o mesmo que um cano. Os pipes conectam stdout a stdin sem envolver nada que se pareça com nomes de arquivos. A substituição de processos é mais flexível, pois você pode ter várias dessas substituições em uma linha de comando, mas exige que o comando principal abra arquivos pelo nome (por exemplo, cat em vez de echo ).

Você pode emular um pipe com a substituição do processo fazendo algo assim:

echo foo > >(cat -n)
    
por 29.05.2017 / 10:10
4

Para completar

cmd1 >(cmd2)

é basicamente o mesmo que

cmd1 | cmd2

no shell yash e somente esse shell.

Nesse shell, >(cmd) é processo redirecionamento ao contrário do >(cmd) de ksh / bash / zsh , que é processo substituição .

Não é estritamente equivalente, porque em cmd1 >(cmd2) , yash não aguarda cmd2 , então você pode descobrir que:

$ yash -c 'echo A >(cat); echo B'
B
A
$ yash -c 'echo A | cat; echo B'
A
B

Por contraste, o processo substituição expande para um caminho de arquivo (normalmente um canal nomeado ou um /dev/fd/<x> onde <x> é um fd para um canal que foi criado anteriormente) que, quando aberto para gravação permitirá enviar a saída para cmd .

Enquanto a substituição do processo foi introduzida por ksh , nesse shell, você não pode passá-los como argumento para redirecionamentos.

ksh -c 'cmd1 > >(cmd2)'

para emular o redirecionamento do processo yash não funcionará. Lá, você deve passar esse nome de arquivo resultante da substituição como argumento para um comando como em:

ksh -c 'diff <(echo a) <(echo b)'

Funcionará em bash e zsh .

No entanto, em bash , como no redirecionamento de processo de yash , o shell não aguarda o comando ( cmd2 ). Então:

$ bash -c 'echo A > >(cat); echo B'
B
A

ksh de substituição do processo pode ser emulado com yash com:

cmd1 /dev/fd/5 5>(cmd2)   

Como:

diff /dev/fd/3 3<(echo a) /dev/fd/4 4<(echo b)
    
por 29.05.2017 / 17:51
3
$ echo 1 >(cat > /dev/null)
1 /dev/fd/63
$ echo echo >(cat /dev/null)
echo /dev/fd/63

# We can trace how the commands are executed
# so long as we avoid using shell builtin commands,
# and run the equivalent external program instead, i.e. /usr/bin/echo

$ strace -f -e execve bash -c '/usr/bin/echo >(cat /dev/null)'
execve("/usr/bin/bash", ["bash", "-c", "/usr/bin/echo >(cat /dev/null)"], [/* 56 vars */]) = 0
strace: Process 4213 attached
[pid  4212] execve("/usr/bin/echo", ["/usr/bin/echo", "/dev/fd/63"], [/* 56 vars */]) = 0
strace: Process 4214 attached
[pid  4214] execve("/usr/bin/cat", ["cat", "/dev/null"], [/* 56 vars */]/dev/fd/63
) = 0
[pid  4212] +++ exited with 0 +++
[pid  4214] +++ exited with 0 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=4214, si_uid=1001, si_status=0, si_utime=0, si_stime=0} ---
+++ exited with 0 +++

# Apparently, the order of evaluation is arranged so this works nicely:

$ echo 1 > >(cat > /dev/null)
$
    
por 29.05.2017 / 10:17