Ctrl-C con due comandi simultanei in bash


15

Voglio eseguire due comandi contemporaneamente in bash su una macchina Linux. Pertanto nel mio ./execute.shscript bash ho inserito:

command 1 & command 2
echo "done"

Tuttavia, quando voglio interrompere lo script bash e premere Ctrl+ C, viene interrotto solo il secondo comando. Il primo comando continua a funzionare. Come posso assicurarmi che lo script bash completo sia interrotto? O in ogni caso, come posso interrompere entrambi i comandi? Perché in questo caso, non importa quanto spesso premo Ctrl+ Cil comando continua a funzionare e sono costretto a chiudere il terminale.


Trasforma la domanda di follow-up in una domanda separata e rimuovila qui. Ora non è chiaro per un visitatore se entrambe le domande hanno ricevuto risposta o solo la domanda iniziale.
Zelda,

Risposte:


17

Se digiti

command 1 & command 2

questo è uguale a

command 1 &
command 2

cioè questo eseguirà il primo comando in background e quindi eseguirà il secondo comando in primo piano. Soprattutto questo significa che il tuo echo "done"è stampato dopo aver command 2finito anche se command 1è ancora in esecuzione.

Probabilmente vuoi

command 1 &
command 2 &
wait
echo "done"

Questo eseguirà entrambi i comandi in background e attenderà il completamento di entrambi.


Se premi CTRL-C, questo invierà solo il segnale SIGINT al processo in primo piano, cioè command 2nella tua versione o waitnella mia versione.

Suggerirei di impostare una trappola come questa:

#!/bin/bash

trap killgroup SIGINT

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

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

loop 1 &
loop 2 &
wait

Con la trap il segnale SIGINT prodotto da CTRL-C viene intrappolato e sostituito dalla killgroupfunzione, che uccide tutti quei processi.


La ringrazio per la risposta! Tuttavia ho una domanda di follow-up. Ora ho il seguente script: trap killgroup SIGINT killgroup () {echo killing ... kill 0} command001 command1 & command2 Ho omesso il comando wait perché lo script è autorizzato a continuare al termine di command2. Tuttavia sembra che il comando 1 inizi già quando eseguo lo script. Posso risolvere questo? Grazie
maero21

command1 inizia direttamente dopo che command001 è terminato. è possibile utilizzare set -xall'inizio dello script per stampare i comandi eseguiti.
michas

6

Quando si inserisce un comando in background da uno script, il PID non viene visualizzato sullo schermo. È possibile utilizzare la variabile incorporata$! che memorizza il PID dell'ultimo processo in modo da poter acquisire il PID di command1.

command1 &
echo $!

farebbe eco al PID di command1.

Bash fornisce anche il trap incorporato che è possibile utilizzare per registrare una sequenza di comandi da eseguire quando vengono ricevuti determinati segnali. Puoi usarlo per catturare SIGINT e uccidere command1 prima di uscire dallo script principale, ad es

#!/bin/bash

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

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

Ora, mentre il comando 2 è in esecuzione, colpire Ctrl Ccauserà sia SIGINT sia command1 che command2.


È inoltre possibile utilizzare la %nsintassi per terminare specifici lavori in background in questa shell. Di solito si emette kill %1per uccidere l'ultimo e l'unico processo in background. Con più processi in background, utilizzare jobsper visualizzare prima l'elenco.
9000

@ 9000: Sì, ma l'OP sta eseguendo i suoi comandi in uno script (./execute.sh), quindi far funzionare i lavori è sicuramente molto più problematico?

@lain: certamente. Ho perso il punto sulla fuga da una sceneggiatura.
9000

5

Le versioni più recenti di GNU Parallel faranno quello che vuoi:

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

O se commandrimane lo stesso:

parallel command ::: 1 2
echo done

Guarda il video introduttivo per una rapida introduzione: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

Cammina attraverso il tutorial (man parallel_tutorial). La riga di comando ti ama per questo.


1

Ctrl+ Cinvia un segnale SIGINT al tuo processo frontale, che è command2. command1viene eseguito in background, quindi non interessato dal flusso di input.

Quando digiti command1 &, bash dovrebbe darti il ​​processo PID, qualcosa del genere [1234]. Per terminare questo processo, puoi usare kill 1234. Ora, se hai i PID di entrambi command1e command2(dai un'occhiata ps -ef), puoi usarli killper terminarli tutti:

kill pid1 pid2 pid3 ...

Un piccolo trucco sarebbe quello di eseguire entrambi i comandi in background, con:

command1 & command2 &

Bash ti darà entrambi i PID, pronti per essere ucciso. Un altro trucco sarebbe quello di riportare command1in primo piano una volta ucciso command2:

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

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

Maggiori informazioni disponibili qui: http://linuxg.net/how-to-manage-background-and-foreground-processes/

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.