GNU/Linux >> Tutoriales Linux >  >> Linux

¿La salida de sustitución del proceso está fuera de orden?

El

echo one; echo two > >(cat); echo three; 

el comando da un resultado inesperado.

Leí esto:¿Cómo se implementa la sustitución de procesos en bash? y muchos otros artículos sobre la sustitución de procesos en Internet, pero no entiendo por qué se comporta de esta manera.

Resultado esperado:

one
two
three

Salida real:

prompt$ echo one; echo two > >(cat); echo three;
one
three
prompt$ two

Además, estos dos comandos deberían ser equivalentes desde mi punto de vista, pero no lo son:

##### first command - the pipe is used.
prompt$ seq 1 5 | cat
1
2
3
4
5
##### second command - the process substitution and redirection are used.
prompt$ seq 1 5 > >(cat)
prompt$ 1
2
3
4
5

¿Por qué creo que deberían ser iguales? Porque ambos conectan el seq salida al cat entrada a través de la canalización anónima:Wikipedia, sustitución de procesos.

Pregunta: ¿Por qué se comporta de esta manera? ¿Dónde está mi error? Se desea una respuesta completa (con una explicación de cómo bash lo hace bajo el capó).

Respuesta aceptada:

Sí, en bash como en ksh (de donde proviene la función), los procesos dentro de la sustitución del proceso no se esperan (antes de ejecutar el siguiente comando en el script).

para un <(...) one, eso suele estar bien como en:

cmd1 <(cmd2)

el shell estará esperando cmd1 y cmd1 normalmente estará esperando cmd2 en virtud de que lee hasta el final del archivo en la tubería que se sustituye, y ese final del archivo generalmente ocurre cuando cmd2 muere Esa es la misma razón por la que varios shells (no bash ) no se moleste en esperar cmd2 en cmd2 | cmd1 .

Para cmd1 >(cmd2) , sin embargo, generalmente ese no es el caso, ya que es más cmd2 que normalmente espera cmd1 allí, por lo general, saldrá después.

Eso está arreglado en zsh que espera cmd2 allí (pero no si lo escribes como cmd1 > >(cmd2) y cmd1 no está integrado, use {cmd1} > >(cmd2) en su lugar como está documentado).

ksh no espera por defecto, pero le permite esperarlo con wait incorporado (también hace que el pid esté disponible en $! , aunque eso no ayuda si haces cmd1 >(cmd2) >(cmd3) )

rc (con el cmd1 >{cmd2} sintaxis), igual que ksh excepto que puede obtener los pids de todos los procesos en segundo plano con $apids .

es (también con cmd1 >{cmd2} ) espera cmd2 como en zsh , y también espera cmd2 en <{cmd2} redirecciones de procesos.

bash hace el pid de cmd2 (o más exactamente de la subcapa, ya que ejecuta cmd2 en un proceso secundario de esa subcapa aunque sea el último comando allí) disponible en $! , pero no te deja esperar.

Si tiene que usar bash , puede solucionar el problema usando un comando que esperará ambos comandos con:

{ { cmd1 >(cmd2); } 3>&1 >&4 4>&- | cat; } 4>&1

Eso hace que ambos cmd1 y cmd2 tienen su fd 3 abierto a una tubería. cat esperará el final del archivo en el otro extremo, por lo que normalmente solo saldrá cuando cmd1 y cmd2 estan muertos. Y el caparazón esperará a ese cat dominio. Podrías verlo como una red para capturar la terminación de todos los procesos en segundo plano (puedes usarlo para otras cosas iniciadas en segundo plano como con & , coprocs o incluso comandos que se ejecutan en segundo plano siempre que no cierren todos los descriptores de archivo como suelen hacer los demonios).

Relacionado:¿Qué hace que los archivos pierdan permisos?

Tenga en cuenta que gracias al proceso de subcapa desperdiciado mencionado anteriormente, funciona incluso si cmd2 cierra su fd 3 (los comandos generalmente no hacen eso, pero algunos como sudo o ssh hacer). Futuras versiones de bash eventualmente puede hacer la optimización allí como en otros shells. Entonces necesitarías algo como:

{ { cmd1 >(sudo cmd2; exit); } 3>&1 >&4 4>&- | cat; } 4>&1

Para asegurarse de que todavía hay un proceso de shell adicional con ese fd 3 abierto esperando ese sudo comando.

Tenga en cuenta que cat no leerá nada (ya que los procesos no escriben en su fd 3). Solo está ahí para la sincronización. Solo hará una read() llamada al sistema que regresará sin nada al final.

De hecho, puedes evitar ejecutar cat usando una sustitución de comando para hacer la sincronización de tubería:

{ unused=$( { cmd1 >(cmd2); } 3>&1 >&4 4>&-); } 4>&1

Esta vez, es el shell en lugar de cat que está leyendo desde la tubería cuyo otro extremo está abierto en fd 3 de cmd1 y cmd2 . Estamos usando una asignación de variable, por lo que el estado de salida de cmd1 está disponible en $? .

O podría hacer la sustitución del proceso a mano, y luego podría incluso usar el sh de su sistema ya que eso se convertiría en la sintaxis estándar de shell:

{ cmd1 /dev/fd/3 3>&1 >&4 4>&- | cmd2 4>&-; } 4>&1

aunque tenga en cuenta, como se señaló anteriormente, que no todos los sh las implementaciones esperarían cmd1 después de cmd2 ha terminado (aunque eso es mejor que al revés). Esa vez, $? contiene el estado de salida de cmd2; aunque bash y zsh hacer cmd1 Estado de salida disponible en ${PIPESTATUS[0]} y $pipestatus[1] respectivamente (ver también el pipefail opción en algunas conchas así que $? puede informar la falla de los componentes de la tubería que no sean el último)

Tenga en cuenta que yash tiene problemas similares con su proceso de redireccionamiento rasgo. cmd1 >(cmd2) se escribiría cmd1 /dev/fd/3 3>(cmd2) allí. Pero cmd2 no se espera y no puede usar wait esperarlo tampoco y su pid no está disponible en el $! variable tampoco. Usaría las mismas soluciones alternativas que para bash .


Linux
  1. ¿Nuevo proceso principal cuando muere el proceso principal?

  2. ¿Qué sucede con la salida de un proceso que ha sido repudiado y ha perdido su terminal?

  3. ¿Salida del comando "último"?

  4. ¿Cómo cambiar la redirección de salida de un proceso en ejecución?

  5. ¿Cerrar la salida estándar (>&-)?

Diff ¿Dónde las líneas son en su mayoría iguales pero desordenadas?

¿La forma portátil (posix) de lograr la sustitución de procesos?

SIGTERM vs SIGKILL:¿Cuál es la diferencia?

Sustitución de procesos:una forma poco común pero avanzada de redirección de entrada/salida en Linux

Ejecutar proceso con salida en tiempo real en PHP

Linux:averigüe qué proceso está utilizando toda la RAM?