Ctrl-C con dos comandos simultáneos en bash

15

Quiero ejecutar dos comandos simultáneamente en bash en una máquina Linux. Por lo tanto, en mi ./execute.shscript bash pongo:

command 1 & command 2
echo "done"

Sin embargo, cuando quiero detener el script bash y presionar Ctrl+ C, solo se detiene el segundo comando. El primer comando sigue ejecutándose. ¿Cómo me aseguro de que se detenga el script bash completo? O, en cualquier caso, ¿cómo detengo ambos comandos? Porque en este caso, no importa con qué frecuencia presione Ctrl+, Cel comando sigue ejecutándose y me veo obligado a cerrar el terminal.

maero21
fuente
Haga la pregunta de seguimiento en una pregunta separada y elimínela aquí. Ahora no está claro para un visitante si ambas preguntas fueron respondidas, o solo la pregunta inicial.
Zelda

Respuestas:

17

Si escribes

command 1 & command 2

esto es igual a

command 1 &
command 2

es decir, esto ejecutará el primer comando en segundo plano y luego ejecutará el segundo comando en primer plano. Especialmente esto significa que echo "done"se imprime una vez command 2terminado, incluso si command 1todavía se está ejecutando.

Probablemente quieras

command 1 &
command 2 &
wait
echo "done"

Esto ejecutará ambos comandos en segundo plano y esperará a que ambos se completen.


Si presiona CTRL-C, esto solo enviará la señal SIGINT al proceso de primer plano, es decir, command 2en su versión o waiten mi versión.

Sugeriría establecer una trampa como esta:

#!/bin/bash

trap killgroup SIGINT

killgroup(){
  echo killing...
  kill 0
}

loop(){
  echo $1
  sleep $1
  loop $1
}

loop 1 &
loop 2 &
wait

Con la trampa, la señal SIGINT producida por CTRL-C queda atrapada y reemplazada por la killgroupfunción, que mata todos esos procesos.

michas
fuente
¡Gracias por su respuesta! Sin embargo, tengo una pregunta de seguimiento. Ahora tengo el siguiente script: trap killgroup SIGINT killgroup () {echo kill ... kill 0} command001 command1 & command2 He omitido el comando wait porque el script puede continuar cuando termine el comando2. Sin embargo, parece que el comando 1 ya comienza cuando ejecuto el script. ¿Puedo arreglar esto? Gracias
maero21
command1 comienza directamente después de que command001 haya finalizado. puede usar set -xal comienzo de su script para imprimir los comandos que se ejecutan.
michas
6

Al colocar un comando en segundo plano desde un script, el PID no se muestra en la pantalla. Puede usar la variable integrada $!que almacena el PID del último proceso para poder capturar el PID del comando1.

command1 &
echo $!

haría eco del PID del comando1.

Bash también proporciona la trampa incorporada que puede usar para registrar una secuencia de comandos para ejecutar cuando se reciben señales particulares. Puede usar esto para capturar el comando SIGINT y kill1 antes de salir del script principal, por ejemplo

#!/bin/bash

onINT() {
echo "Killing command1 $command1PID too"
kill -INT "$command1PID"
exit
}

trap "onINT" SIGINT
command1 &
command1PID="$!"
comamnd2
echo Done

Ahora, mientras el comando 2 se está ejecutando, presionar Ctrl Chará que el comando1 y el comando2 se envíen SIGINT.

Comunidad
fuente
También puede usar la %nsintaxis para eliminar trabajos de fondo específicos en este shell. Por lo general, emite kill %1para matar el último y único proceso en segundo plano. Con más procesos en segundo plano, use jobspara ver primero la lista.
9000
@ 9000: Sí, pero el OP está ejecutando sus comandos en una secuencia de comandos (./execute.sh), ¿entonces hacer que los trabajos funcionen es mucho más problemático?
@lain: ciertamente. Perdí el punto sobre la ejecución de un script.
9000
5

Las versiones más nuevas de GNU Parallel harán lo que quieras:

parallel ::: "command 1" "command 2"
echo "done"

O si commandsigue siendo el mismo:

parallel command ::: 1 2
echo done

Mire el video de introducción para una introducción rápida: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

Recorre el tutorial (man parallel_tutorial). Usted ordena línea con amor por ello.

Ole Tange
fuente
1

Ctrl+ Cenvía una señal SIGINT a su proceso frontal, que es command2. command1se ejecuta en segundo plano, por lo tanto, no se ve afectado por la secuencia de entrada.

Cuando command1 &escribes, bash debería darte el proceso 'PID, algo así como [1234]. Para matar este proceso, puedes usar kill 1234. Ahora, si tiene los PID de ambos command1y command2(eche un vistazo ps -ef), puede usar killpara terminarlos todos:

kill pid1 pid2 pid3 ...

Un pequeño truco sería ejecutar ambos comandos en segundo plano, con:

command1 & command2 &

Bash te dará los dos PID, listos para que te maten. Otro truco sería volver a poner command1en primer plano una vez que mataste command2:

command1 & command2
# Hit Ctrl+C : command2 terminates.

fg # Bring back command1 to foreground.
# Hit Ctrl+C again, command1 terminates.

Más información disponible aquí: http://linuxg.net/how-to-manage-background-and-foreground-processes/

John WH Smith
fuente