Orden de salida con sustitución de proceso

11

Esto es lo que suelo hacer para ejecutar grepy wcen un archivo sin tener que escanearlo dos veces

<file.txt  tee >(grep LITERAL) >(wc -l) >/dev/null

Sin embargo, esto produce

EXEC LITERAL
32

a veces y

32
EXEC LITERAL

en otros tiempos. (La salida de grepprecede a la salida de wcen la primera instancia y viceversa en la segunda).

Por otro lado, con redirecciones y descriptores de archivos.

{ { <file.txt tee /dev/fd/3 | grep LITERAL >&4; } 3>&1 | wc -l ;} 4>&1 

Siempre me parece

EXEC LITERAL
32

Prefiero que el orden de salida sea predecible, pero ¿está garantizado con el segundo enfoque?

iruvar
fuente

Respuestas:

4

En ambos

<file.txt  tee >(grep LITERAL) >(wc -l) >/dev/null

Y:

{ { <file.txt tee /dev/fd/3 | grep LITERAL >&4; } 3>&1 | wc -l ;} 4>&1

Todos tee, grepy wcse inician simultáneamente. Lo que importa es lo que sucede al final.

wcsolo imprimirá el resultado cuando vea el final del archivo en su entrada estándar. En el primer caso, es cuando teesale, porque luego teese cerrará fden el otro extremo de la tubería que wcestá leyendo (iniciado por la sustitución del proceso). No hay garantía de que grephabrá leído toda su entrada para ese momento, y mucho menos escrito su salida (dado que las tuberías pueden contener una gran cantidad de datos y que wcprobablemente sea más rápido que grep)

En el segundo caso, wcveremos el final del archivo cuando todos los escritores en la tubería desde la que está leyendo hayan cerrado su extremo de la tubería. En ese caso, sin embargo, hay varios escritores. tee(a través de su fd abierto /dev/fd/3y a través de su fd 3) y grepque también tiene sus fd3 abiertos a la tubería wc(aunque no lo está utilizando, y mucho menos escribirle). El interno {probablemente causará un proceso de subshell adicional que también tendrá un fd3 abierto y esperará a ambos teey grep.

Eso significa que wcsolo escribirá su número de línea después de grephaber salido.

Si lo hubiera escrito de la manera correcta, es al cerrar los archivos que no necesitaban abrirse:

{ { <file.txt tee /dev/fd/3 4>&- | 
   grep LITERAL >&4 3>&- 4>&-; } 3>&1 | wc -l 4>&-;} 4>&1

Entonces el pedido no se habría garantizado en shells que optimizan el proceso de subshell. Sin embargo, el único shell que sé que sí es, ksh93pero ksh93usa pares de conectores para tuberías, por /dev/fd/3lo que no funcionará allí en Linux al menos.

Para ver qué procesos se están ejecutando, puede reemplazar grepcon ps:

$ { { <file.txt tee /dev/fd/3 4>&- | ps -H >&4 3>&- 4>&-; } 3>&1 | wc -l 4>&-;} 4>&1
  PID TTY          TIME CMD
 8727 pts/5    00:00:00 bash
 8815 pts/5    00:00:00   bash
 8817 pts/5    00:00:00     tee
 8818 pts/5    00:00:00     ps
 8816 pts/5    00:00:00   wc

Con bash, puede ver ese proceso de shell adicional, y puede ver que también tiene la tubería abierta en fd 3 con:

$ (p=$BASHPID; { { <file.txt tee /dev/fd/3 4>&- | lsof -ag "$p" -d3 >&4 3>&- 4>&-; } 3>&1 | wc -l 4>&-;} 4>&1)
COMMAND  PID PGID     USER   FD   TYPE DEVICE SIZE/OFF   NODE NAME
bash    9843 9842 chazelas    3w  FIFO    0,8      0t0 153304 pipe
tee     9845 9842 chazelas    3w  FIFO    0,8      0t0 153304 pipe
lsof    9846 9842 chazelas    3r   DIR    0,3        0      1 /proc
Stéphane Chazelas
fuente
Gracias. En su "ejemplo apropiado", ¿qué grep LITERAL >&4 3>&- 4>&-significa que el fd 4 parece ser usado y cerrado?
iruvar
@ 1_CR, después >&4, abreviatura de 1>&4, grepfd 1 y 4 apuntan al mismo recurso (el stdout inicial del shell). grepno necesita tener su fd 4 abierto a nada. No hace nada con él, así que lo 4>&-
cerramos
Esa última línea de comando es magia críptica.
-1

Para obtener un pedido predecible use

(<file.txt  tee >(grep LITERAL) >(wc -l) >/dev/null)|sort
Thorsten Staerk
fuente
Quizás no fui lo suficientemente claro. Me refería al orden predecible en términos del orden de las salidas del comando (es decir, salida de grep antes de salida de wc). No necesito la salida combinada ordenada
iruvar
acabo de encontrar gnu.org/software/bash/manual/bashref.html#Command-Grouping , me dice que con los operadores {} te aseguras (en este caso) de que primero hagas <file.txt tee / dev / fd / 3 | grep LITERAL> & 4; y cuando se hace esto, llama a wc, por lo que para responder a su pregunta original, sí, a mi entender
Thorsten Staerk
1
@ThorstenStaerk, ¿podría agregar la información adicional que encontró a su respuesta?
terdon