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
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 quecd
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).
[[ -p <(date) ]] && echo true
. Esto producetrue
cuando lo ejecuto con bash 4.4 o 3.2.