Sustitución de procesos y canalización

Me preguntaba cómo entender lo siguiente:

Conectar la salida estándar de un comando al estándar de otro es una técnica poderosa. Pero, ¿qué sucede si necesita canalizar la salida estándar de varios comandos? Aquí es donde entra en juego la sustitución de procesos.

En otras palabras, ¿puede la sustitución de procesos hacer lo que pueda hacer la tubería?

¿Qué puede hacer el proceso de sustitución, pero la tubería no?

Respuesta

Una buena forma de asimilar La diferencia entre ellos es experimentar un poco en la línea de comandos. A pesar de la similitud visual en el uso del carácter <, hace algo muy diferente a una redirección o canalización.

Usemos el date comando para probar.

$ date | cat Thu Jul 21 12:39:18 EEST 2011 

Este es un ejemplo inútil pero muestra que cat aceptó la salida de date en STDIN y la escupió. Los mismos resultados se pueden lograr mediante la sustitución del proceso:

$ cat <(date) Thu Jul 21 12:40:53 EEST 2011 

Sin embargo, lo que sucedió detrás de escena fue diferente. En lugar de recibir un flujo STDIN, cat en realidad se le pasó el nombre de un archivo que necesitaba abrir y leer. Puedes ver este paso usando echo en lugar de cat.

$ echo <(date) /proc/self/fd/11 

Cuando cat recibió el nombre del archivo, leyó el contenido del archivo por nosotros. Por otro lado, echo solo nos mostró el nombre del archivo que se pasó. Esta diferencia se vuelve más obvia si agrega más sustituciones:

$ cat <(date) <(date) <(date) Thu Jul 21 12:44:45 EEST 2011 Thu Jul 21 12:44:45 EEST 2011 Thu Jul 21 12:44:45 EEST 2011 $ echo <(date) <(date) <(date) /proc/self/fd/11 /proc/self/fd/12 /proc/self/fd/13 

It es posible combinar la sustitución de procesos (que genera un archivo) y la redirección de entrada (que conecta un archivo a STDIN):

$ cat < <(date) Thu Jul 21 12:46:22 EEST 2011 

Se ve más o menos igual pero esta vez a cat se le pasó el flujo STDIN en lugar de un nombre de archivo. Puede ver esto probándolo con echo:

$ echo < <(date) <blank> 

Dado que echo no lee STDIN y no se pasó ningún argumento, no obtenemos nada.

Las canalizaciones y las redirecciones de entrada envían contenido al flujo STDIN. La sustitución de procesos ejecuta los comandos, guarda su salida en un archivo temporal especial y luego pasa ese nombre de archivo en lugar del comando. Cualquier comando que esté utilizando lo trata como un nombre de archivo. Tenga en cuenta que el archivo creado no es un archivo normal, sino una tubería con nombre que se elimina automáticamente una vez que ya no se necesita.

Comentarios

  • Si entendido correctamente, tldp.org/LDP/abs/html/process-sub.html#FTN.AEN18244 dice que la sustitución de procesos crea archivos temporales, no conductos con nombre. Hasta donde yo sé, named no crea archivos temporales. Escribir en la tubería nunca implica escribir en el disco: stackoverflow.com/a/6977599/788700
  • Sé que esta respuesta es legítima ‘ porque usa la palabra grok : D
  • @Adobe puede confirmar si el proceso de sustitución de archivos temporal produce una tubería con nombre con: [[ -p <(date) ]] && echo true. Esto produce true cuando lo ejecuto con bash 4.4 o 3.2.

Answer

Aquí hay tres cosas que puede hacer con la sustitución de procesos que son imposibles de otra manera.

Múltiples entradas de proceso

diff <(cd /foo/bar/; ls) <(cd /foo/baz; ls) 

Allí simplemente no hay forma de hacer esto con tuberías.

Preservando STDIN

Supongamos que tiene lo siguiente:

curl -o - http://example.com/script.sh #/bin/bash read LINE echo "You said ${LINE}!" 

Y quieres ejecutarlo directamente. Lo siguiente falla estrepitosamente. Bash ya está usando STDIN para leer el script, por lo que otras entradas son imposibles.

curl -o - http://example.com/script.sh | bash 

Pero de esta manera funciona perfectamente.

bash <(curl -o - http://example.com/script.sh) 

Sustitución de procesos de salida

También tenga en cuenta que la sustitución de procesos también funciona al revés. Entonces puedes hacer algo como esto:

(ls /proc/*/exe >/dev/null) 2> >(sed -n \ "/Permission denied/ s/.*\(\/proc.*\):.*/\1/p" > denied.txt ) 

Ese es un ejemplo un poco complicado, pero envía stdout a /dev/null, mientras canaliza stderr a un script sed para extraer los nombres de los archivos para los que un » Permiso denegado » se mostró el error y luego envía ESOS resultados a un archivo.

Tenga en cuenta que el primer comando y la redirección stdout están entre paréntesis ( subshell ) para que solo el resultado de ESE comando se envíe a /dev/null y no se meta con el resto de la línea.

Comentarios

  • Es ‘ s vale la pena señalar que en el diff Por ejemplo, es posible que desee preocuparse por el caso en el que cd podría fallar: diff <(cd /foo/bar/ && ls) <(cd /foo/baz && ls).
  • » mientras canaliza stderr «: isn ‘ t el señalar que esto no es una canalización, sino que está pasando por un archivo FIFo.
  • @Gauthier no; el comando se sustituye no por un quince, sino por una referencia al descriptor del archivo. Entonces » echo < (echo) » debería producir algo como » / dev / fd / 63 «, que es un dispositivo de caracteres especiales que lee o escribe desde el FD número 63.

Answer

Supongo que estás hablando de bash o algún otro shell avanzado, porque el posix shell no tiene sustitución de proceso .

bash informes de página de manual:

Sustitución de procesos
La sustitución de procesos se admite en sistemas que admiten canalizaciones con nombre (FIFO) o el método / dev / fd para nombrar archivos abiertos. Toma la forma de < (lista) o> (lista). La lista de procesos se ejecuta con su entrada o salida conectada a un FIFO o algún archivo en / dev / fd. El nombre de este archivo se pasa como argumento al comando actual como resultado de la expansión. Si se utiliza la forma> (lista), escribir en el archivo proporcionará entrada para la lista. Si se utiliza el formulario < (lista), el archivo pasado como argumento debe leerse para obtener el resultado de la lista.

Cuando esté disponible, proceso de sustitución se realiza simultáneamente con la expansión de parámetros y variables, la sustitución de comandos y la expansión aritmética.

En otras palabras, y desde un punto de vista práctico, puede utilizar una expresión como la siguiente

<(commands) 

como nombre de archivo para otros comandos que requieren un archivo como parámetro. O puede usar la redirección para dicho archivo:

while read line; do something; done < <(commands) 

Volviendo a su pregunta, me parece que la sustitución de procesos y las tuberías no tienen mucho en común.

Si desea canalizar en secuencia la salida de varios comandos, puede utilizar una de las siguientes formas:

(command1; command2) | command3 { command1; command2; } | command3 

pero también puede usar la redirección en la sustitución de procesos

command3 < <(command1; command2) 

finalmente, si command3 acepta un parámetro de archivo (en sustitución de stdin )

command3 <(command1; command2) 

Comentarios

  • entonces < ( ) y < < () hace el mismo efecto, ¿verdad?
  • @solfish: no exactamente: el primero puede usarse donde se espere un nombre de archivo, el segundo es una redirección de entrada para ese nombre de archivo

Respuesta

Si un El comando toma una lista de archivos como argumentos y procesa esos archivos como entrada (o salida, pero no comúnmente), cada uno de esos archivos puede ser una tubería con nombre o un pseudoarchivo / dev / fd proporcionado de forma transparente por la sustitución del proceso:

$ sort -m <(command1) <(command2) <(command3) 

Este «canalizará» la salida de los tres comandos para ordenar, ya que sort puede tomar una lista de archivos de entrada en la línea de comando.

Comentarios

  • IIRC la < (comando) sintaxis es una función solo de bash.
  • @Philomath: Es ‘ s en ZSH también.
  • Bueno, ZSH lo tiene todo … (o al menos intenta hacerlo).
  • @Philomath: ¿Cómo se implementa la sustitución de procesos en otros shells?
  • @Philomath <(), como muchas funciones avanzadas de shell, era originalmente una función ksh y fue adoptada por bash y zsh. psub es específicamente una función de pez, nada que ver con POSIX.

Respuesta

Cabe señalar que la sustitución de procesos no se limita a la forma <(command), que utiliza la salida de command como expediente. También puede tener el formato >(command) que alimenta un archivo como entrada a command. Esto también se menciona en la cita del manual bash en la respuesta de @enzotib.

Para el date | cat ejemplo anterior, un comando que usa la sustitución de proceso del formulario >(command) para lograr el mismo efecto sería,

date > >(cat) 

Tenga en cuenta que el > antes de que >(cat) sea necesario. Esto se puede ilustrar claramente de nuevo con echo como en la respuesta de @Caleb.

$ echo >(cat) /dev/fd/63 

Entonces, sin el > adicional, date >(cat) sería el igual que date /dev/fd/63 que imprimirá un mensaje en stderr.

Suponga que tiene un programa que solo toma nombres de archivos como parámetros y no procesa stdin o stdout.Usaré el script demasiado simplificado psub.sh para ilustrar esto. El contenido de psub.sh es

#!/bin/bash [ -e "$1" -a -e "$2" ] && awk "{print $1}" "$1" > "$2" 

Básicamente, prueba que ambos argumentos son archivos (no necesariamente regulares files) y si este es el caso, escriba el primer campo de cada línea de "$1" en "$2" usando awk. Luego, un comando que combina todo lo mencionado hasta ahora es,

./psub.sh <(printf "a a\nc c\nb b") >(sort) 

Esto imprimirá

a b c 

y es equivalente a

printf "a a\nc c\nb b" | awk "{print $1}" | sort 

pero lo siguiente no funcionará, y tenemos que usar la sustitución de procesos aquí,

printf "a a\nc c\nb b" | ./psub.sh | sort 

o su forma equivalente

printf "a a\nc c\nb b" | ./psub.sh /dev/stdin /dev/stdout | sort 

Si ./psub.sh también lee stdin además de lo mencionado anteriormente, entonces no existe tal forma equivalente, y en ese caso no hay nada que podamos usar en lugar de la sustitución del proceso (por supuesto, también puede usar un pipe con nombre o archivo temporal, pero esa es otra historia).

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *