$ 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 thing
interior 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
a qual 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 cat
processo na substituição do processo.
Modificando ligeiramente seu código de exemplo:
$ echo 101 > >(cat)
Isso produziria apenas 101
na saída padrão (a saída echo
seria redirecionada para o arquivo que serve como entrada 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 lastpipe
opçã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 yash
shell, e somente esse shell.
Nesse shell, >(cmd)
está o processoredirecionamentoem oposição ao >(cmd)
de ksh
/ bash
/ zsh
que é processosubstituição.
Não é estritamente equivalente, porque in cmd1 >(cmd2)
, yash
nã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 yash
o 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 bash
e zsh
.
No entanto, bash
assim como no yash
redirecionamento de processo de for, o shell não espera pelo comando ( cmd2
). Então:
$ bash -c 'echo A > >(cat); echo B'
B
A
ksh
a 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/NN
caminho 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 stdout
sem stdin
envolver 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, cat
em 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)
$