¿Qué es exactamente <() en bash (y = () en zsh)?

36

Estoy bastante cómodo con bash, pero recientemente terminé en una sustitución que no conocía.

¿Qué es exactamente <(<command>)en bash? ¿Cómo se compara con el =(<command>)in zsh?

Entiendo que esto tiene algo que ver con los descriptores de archivos predeterminados. En mi computadora

echo <()

vuelve /proc/self/fd/11, lo que descubrí que es una copia del script STDOUT, pero esto todavía me parece bastante confuso.

Henrique Barcelos
fuente

Respuestas:

52

Esto se llama sustitución de proceso.

La <(list)sintaxis es compatible con ambos bashy zsh. Proporciona una manera de pasar la salida de un comando ( list) a otro comando cuando no se puede utilizar una tubería ( |). Por ejemplo, cuando un comando simplemente no admite la entrada STDINo necesita la salida de varios comandos:

diff <(ls dirA) <(ls dirB)

<(list)conecta la salida de listcon un archivo /dev/fd, si es compatible con el sistema, de lo contrario se usa una tubería con nombre (FIFO) (que también depende del soporte del sistema; ninguno de los manuales dice qué sucede si ambos mecanismos no son compatibles, presumiblemente aborta con un error). El nombre del archivo se pasa como argumento en la línea de comando.


zshadicionalmente admite =(list)como posible reemplazo para <(list). Con =(list)un archivo temporal se utiliza en lugar de un archivo /dev/fdo un FIFO. Se puede usar como reemplazo <(list)si el programa necesita buscar en la salida.

Según el manual de ZSH, también puede haber otros problemas con el <(list)funcionamiento:

El =formulario es útil ya que tanto la /dev/fdimplementación de canalización con nombre como la de <(...)tienen inconvenientes. En el primer caso, algunos programas pueden cerrar automáticamente el descriptor de archivo en cuestión antes de examinar el archivo en la línea de comando, especialmente si esto es necesario por razones de seguridad, como cuando el programa se está ejecutando setuid. En el segundo caso, si el programa no abre realmente el archivo, la subshell que intenta leer o escribir en la tubería bloqueará (en una implementación típica, los diferentes sistemas operativos pueden tener un comportamiento diferente) para siempre y tendrá que eliminarse explícitamente . En ambos casos, el shell realmente proporciona la información usando una tubería, de modo que los programas que esperan buscar (ver la página del manual lseek(2)) en el archivo no funcionarán.

Adaephon
fuente
Esto me ayudó a entender por qué MacOS pfctl -f <(echo "pf rules")diría que el descriptor de archivo es incorrecto. El uso de zsh y = (echo "pf rules") en su lugar funciona.
johnnyB
9

Tenga en cuenta que esta es una respuesta bash, no zsh.

Hay casos en bash donde no puedes usar tuberías:

some_command | some_other_command

Debido a que las tuberías introducen subcapas para cada componente de la tubería, cuando las subcapas salen, cualquier efecto secundario en el que confiaba desaparecería. Por ejemplo, este ejemplo artificial:

cat file | while read line; do ((count++)); done
echo $count

mostrará una línea en blanco, porque la $countvariable no existe en el shell actual.

Una sustitución del proceso bash le permite evitar este enigma al permitirle leer desde la salida "some_command" como lo haría desde un archivo

while read line; do ((count++)); done < <(cat file)
# ....................................1.2
echo $count   # the variable *does* exist in the current shell

(1) es una redirección de entrada normal. (2) es el comienzo de la <()sintaxis de sustitución del proceso.

Glenn Jackman
fuente
2
= (cmdlist) en zsh tiene casi el mismo efecto que <(cmdlist) en bash pero crea (y elimina cuando está listo) un archivo temporal con la salida de cmdlist para la redirección. Esto es bueno cuando la búsqueda se realiza potencialmente en el programa. <(cmdlist) también es conocido por zsh.
Gombai Sándor