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

Por que existe um "/dev/fd/63" na saída de "echo 123 >(cat)"?
$ echo 123 | cat 
123

está fazendo o que eu esperava, ambos os comandos são executados 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, obtenho o seguinte:

$ 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

Achei command1 >(command2)que fosse igual command1 | command2, mas em command1 >(command2)cada comando está dentro de um shell diferente, portanto devem ter a mesma saída. Onde estou errado?

Responder1

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 thinginterior da substituição.

O seguinte seria um exemplo melhor de seu uso:

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

Isso classificaria o arquivo ~/.profilee enviaria a saída para cat -na qual enumeraria as linhas e armazenaria o resultado em /tmp/out.

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

Modificando ligeiramente seu código de exemplo:

$ echo 101 > >(cat)

Isso produziria apenas 101na saída padrão (a saída echoseria redirecionada para o arquivo que serve como entrada cate catproduziria o conteúdo desse arquivo na saída padrão).


Observe também que, no cmd1 | cmd2pipeline, cmd2pode não estar sendo executado no mesmo shell que cmd1(dependendo da implementação do shell que você está usando). ksh93funciona da maneira que você descreve (mesmo shell), enquanto bashcria um subshell para cmd2(a menos que sua lastpipeopção de shell esteja definida e o controle de trabalho não esteja ativo).

Responder2

Para completar

cmd1 >(cmd2)

é basicamente o mesmo que

cmd1 | cmd2

no yashshell, e somente esse shell.

Nesse shell, >(cmd)está o processoredirecionamentoem oposição ao >(cmd)de ksh/ bash/ zshque é processosubstituição.

Não é estritamente equivalente, porque in cmd1 >(cmd2), yashnão espera 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

Em contrapartida, o processosubstituiçãoexpande para um caminho de arquivo (normalmente um pipe nomeado ou um /dev/fd/<x>where <x>é um fd para um pipe que foi criado anteriormente) que, quando aberto para gravação, permitirá enviar a saída para cmd.

Embora a substituição de processo tenha sido introduzida por ksh, nesse shell, você não pode passá-los como argumento para redirecionamentos.

ksh -c 'cmd1 > >(cmd2)'

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

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

Funcionará em bashe zsh.

No entanto, bashassim como no yashredirecionamento de processo de for, o shell não espera pelo comando ( cmd2). Então:

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

ksha substituição do processo pode ser emulada com yash:

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

Como:

diff /dev/fd/3 3<(echo a) /dev/fd/4 4<(echo b)

Responder3

Porqueé isso que a substituição de processo faz, faz com que o comando dentro da substituição apareça como um nome de arquivo. Internamente, ele conecta os comandos através de um pipe, fornecendo o /dev/fd/NNcaminho para o comando principal, para que possa abrir o descritor de arquivo já aberto para o pipe.

Não é o mesmo que um cachimbo. Pipes se conectam stdoutsem stdinenvolver nada que se pareça com nomes de arquivos. A substituição de processo é mais flexível porque você pode ter múltiplas substituições em uma linha de comando, mas requer que o comando principal abra arquivos por nome (por exemplo, catem vez de echo).

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

echo foo > >(cat -n)

Responder4

$ 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)
$

informação relacionada