
El siguiente guión no tiene otro propósito que ilustrar esta pregunta.
#!/usr/bin/env zsh
arbitrary_pipeline () {
shuf | tr a-z A-Z
}
tmpdir=$( mktemp -d )
mkfifo $tmpdir/{orig,alt}
{ tee $tmpdir/orig | arbitrary_pipeline > $tmpdir/alt; } &
pid=$!
paste $tmpdir/orig $tmpdir/alt
rm -rf $tmpdir
wait $pid
El script utiliza tee
y dos canalizaciones con nombre para dividir algunas entradas estándar (arbitrarias) en dos flujos, redirige uno de ellos a un canal arbitrario y pasa las entradas a los dos flujos resultantes paste
. Esquemáticamente:
STDIN --- > --.- arbitrary_pipeline -.
\ \
paste `----<FIRST-ARGUMENT> `- <SECOND-ARGUMENT> --> STDOUT
(En este caso arbitrary_pipeline
simplemente mezcla su entrada estándar y la convierte a mayúsculas, pero, como su nombre lo indica, podría ser cualquier cosa).
La salida estándar del script se ve bien, pero el wait
comando siempre falla:
% grep -iP 'z.*s.*h' /usr/share/dict/words | /tmp/test.sh
Nietzsche CITIZENSHIP'S
Zubeneschamali NIETZSCHE
Zubeneschamali's ZUBENESCHAMALI
citizenship CITIZENSHIP
citizenship's ZUBENESCHAMALI'S
/tmp/test.sh:wait:18: pid 26357 is not a child of this shell
¿Qué estoy haciendo mal?
FWIW:
/usr/bin/env zsh --version
# zsh 5.0.7 (x86_64-pc-linux-gnu)
EDICIONES:
- Se agregaron llaves alrededor de la
tee
tubería, según la sugerencia de Jordanm. (Sin embargo, los resultados no cambiaron). - reemplazado
&!
con&
en respuesta al comentario de Stéphane Chazelas. (Nuevamente, los resultados no cambiaron).
Respuesta1
Antes de 5.0.8, zsh
no podía esperar a que hubiera trabajos que ya estaban muertos. Eso se cambió en 5.0.8 en 2014. Vea el cambioallá.
Aquí, puedes simplemente redirigir stderr para /dev/null
ignorar el problema:
wait $pid 2> /dev/null
Tenga en cuenta que en:
{ tee $tmpdir/orig | arbitrary_pipeline > $tmpdir/alt; } &
Como optimización, zsh
no bifurcará un proceso adicional arbitrary_pipeline
, lo ejecutará en el mismo proceso que ejecuta ese subshell iniciado en segundo plano.
paste
no terminará antes de ver EOF en su entrada estándar, siendo su entrada estándar la tubería donde $pid
(y sus hijos, si los hay) están escribiendo en el otro extremo. Por lo tanto, no verá eof hasta que $pid
(y sus hijos) hayan cerrado todos sus descriptores de archivos (normalmente solo stdout) en el extremo de escritura de la tubería. A menos que $pid
cierre explícitamente su salida estándar (lo cual es muy poco común), eso solo sucederá cuando salga.
Lo que eso significa es que paste
, en la mayoría de los casos, no saldrá antes $pid
; sigue siendo una buena idea hacerlo wait
por si acaso.
Tenga en cuenta que aquí puede utilizar a coproc
para evitar los fifos temporales:
coproc arbitrary_pipeline
cat >&p | paste - /dev/fd/3 3<&p &
coproc : close
wait
(tenga en cuenta que wait
también espera la coproc
s).