$ echo 123 | cat
123
está haciendo lo que esperaba, ambos comandos se ejecutan dentro del mismo shell.
Pero cuando los conecto con la >( ... )
expresión, que conecta la salida de un comando en el shell con un segundo en un subshell, obtengo esto:
$ echo 123 >(cat)
123 /dev/fd/63
Esto también es cierto con otros valores:
$ echo 101 >(cat)
101 /dev/fd/63
$ echo $BASHPID >(cat)
3252 /dev/fd/63
Pensé command1 >(command2)
que era lo mismo que command1 | command2
, pero en command1 >(command2)
, cada comando está dentro de un shell diferente, por lo tanto, deberían tener el mismo resultado. ¿Dónde me equivoco?
Respuesta1
La sustitución del proceso >(thing)
será reemplazada por un nombre de archivo. Este nombre de archivo corresponde a un archivo que está conectado a la entrada estándar del thing
interior de la sustitución.
El siguiente sería un mejor ejemplo de su uso:
$ sort -o >(cat -n >/tmp/out) ~/.profile
Esto ordenaría el archivo ~/.profile
y enviaría la salida, cat -n
donde se enumerarían las líneas y se almacenaría el resultado en formato /tmp/out
.
Entonces, para responder a su pregunta: obtiene ese resultado porque echo
obtiene los dos argumentos 123
y /dev/fd/63
. /dev/fd/63
es el archivo conectado a la entrada estándar del cat
proceso en la sustitución del proceso.
Modificando ligeramente su código de ejemplo:
$ echo 101 > >(cat)
Esto produciría solo 101
en la salida estándar (la salida de echo
sería redirigida al archivo que sirve como entrada cat
y cat
produciría el contenido de ese archivo en la salida estándar).
También tenga en cuenta que, en el cmd1 | cmd2
proceso, cmd2
es posible que no se esté ejecutando en el mismo shell que cmd1
(dependiendo de la implementación de shell que esté utilizando). ksh93
funciona de la manera que usted describe (mismo shell), mientras bash
crea un subshell para cmd2
(a menos que su lastpipe
opción de shell esté configurada y el control de trabajo no esté activo).
Respuesta2
Por completitud
cmd1 >(cmd2)
es casi lo mismo que
cmd1 | cmd2
en el yash
caparazón, y solo en ese caparazón.
En ese caparazón, >(cmd)
está el proceso.redireccióna diferencia del >(cmd)
de ksh
// bash
que zsh
es procesosustitución.
No es estrictamente equivalente, porque en cmd1 >(cmd2)
, yash
no espera cmd2
, por lo que puedes encontrar que:
$ yash -c 'echo A >(cat); echo B'
B
A
$ yash -c 'echo A | cat; echo B'
A
B
Por el contrario, el procesosustituciónse expande a una ruta de archivo (normalmente una canalización con nombre o un /dev/fd/<x>
dónde <x>
hay un fd a una canalización que se ha creado de antemano) que, cuando se abre para escritura, permitirá enviar la salida a cmd
.
Si bien la sustitución de procesos fue introducida por ksh
, en ese shell, no puede pasarlos como argumento para las redirecciones.
ksh -c 'cmd1 > >(cmd2)'
emular yash
la redirección de procesos no funcionará. Allí, debes pasar ese nombre de archivo resultante de la sustitución como argumento a un comando como en:
ksh -c 'diff <(echo a) <(echo b)'
Funcionará en bash
y zsh
.
Sin embargo, en la redirección de procesos bash
de like for yash
, el shell no espera el comando ( cmd2
). Entonces:
$ bash -c 'echo A > >(cat); echo B'
B
A
ksh
La sustitución de procesos se puede emular con yash
:
cmd1 /dev/fd/5 5>(cmd2)
Como:
diff /dev/fd/3 3<(echo a) /dev/fd/4 4<(echo b)
Respuesta3
Porqueeso es lo que hace la sustitución del proceso, hace que el comando dentro de la sustitución aparezca como un nombre de archivo. Internamente, conecta los comandos a través de una tubería, proporcionando la /dev/fd/NN
ruta al comando principal, para que pueda abrir el descriptor de archivo ya abierto en la tubería.
No es lo mismo que una pipa. Las tuberías se conectan stdout
sin stdin
involucrar nada que parezca nombres de archivos. El proceso de sustitución es más flexible porque puede tener varias sustituciones en una línea de comando, pero requiere que el comando principal abra archivos por nombre (por ejemplo, cat
en lugar de echo
).
Puedes emular una tubería con sustitución de procesos haciendo algo como esto:
echo foo > >(cat -n)
Respuesta4
$ 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)
$