¿Por qué hay un "/dev/fd/63" en la salida de "echo 123 >(cat)"?

¿Por qué hay un "/dev/fd/63" en la salida de "echo 123 >(cat)"?
$ 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 thinginterior 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 ~/.profiley enviaría la salida, cat -ndonde 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 echoobtiene los dos argumentos 123y /dev/fd/63. /dev/fd/63es el archivo conectado a la entrada estándar del catproceso en la sustitución del proceso.

Modificando ligeramente su código de ejemplo:

$ echo 101 > >(cat)

Esto produciría solo 101en la salida estándar (la salida de echosería redirigida al archivo que sirve como entrada caty catproduciría el contenido de ese archivo en la salida estándar).


También tenga en cuenta que, en el cmd1 | cmd2proceso, cmd2es posible que no se esté ejecutando en el mismo shell que cmd1(dependiendo de la implementación de shell que esté utilizando). ksh93funciona de la manera que usted describe (mismo shell), mientras bashcrea un subshell para cmd2(a menos que su lastpipeopció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 yashcaparazón, y solo en ese caparazón.

En ese caparazón, >(cmd)está el proceso.redireccióna diferencia del >(cmd)de ksh// bashque zshes procesosustitución.

No es estrictamente equivalente, porque en cmd1 >(cmd2), yashno 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 yashla 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 bashy zsh.

Sin embargo, en la redirección de procesos bashde like for yash, el shell no espera el comando ( cmd2). Entonces:

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

kshLa 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/NNruta 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 stdoutsin stdininvolucrar 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, caten 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)
$

información relacionada