Ctrl-C z dwoma jednoczesnymi poleceniami w bash

15

Chcę uruchomić dwa polecenia jednocześnie w bash na komputerze z systemem Linux. Dlatego w ./execute.shskrypcie bash umieściłem:

command 1 & command 2
echo "done"

Jednak gdy chcę zatrzymać skrypt bash i nacisnąć Ctrl+ C, tylko drugie polecenie jest zatrzymane. Pierwsze polecenie jest kontynuowane. Jak upewnić się, że cały skrypt bash został zatrzymany? W każdym razie, jak zatrzymać oba polecenia? Ponieważ w tym przypadku bez względu na to, jak często naciskam Ctrl+, Cpolecenie działa nadal i jestem zmuszony zamknąć terminal.

maero21
źródło
Zmień pytanie uzupełniające w osobne pytanie i usuń tutaj. Teraz dla użytkownika nie jest jasne, czy na oba pytania udzielono odpowiedzi, czy tylko na pytanie wstępne.
Zelda

Odpowiedzi:

17

Jeśli wpiszesz

command 1 & command 2

to jest równe

command 1 &
command 2

tzn. uruchomi pierwsze polecenie w tle, a następnie uruchomi drugie polecenie na pierwszym planie. W szczególności oznacza to, że echo "done"drukarka jest drukowana po command 2zakończeniu, nawet jeśli command 1nadal działa.

Prawdopodobnie chcesz

command 1 &
command 2 &
wait
echo "done"

Spowoduje to uruchomienie obu poleceń w tle i poczekanie na ich zakończenie.


Jeśli naciśniesz CTRL-C, wyśle ​​to tylko sygnał SIGINT do procesu pierwszego planu, tj. command 2W twojej wersji lub waitw mojej wersji.

Sugerowałbym ustawienie takiej pułapki:

#!/bin/bash

trap killgroup SIGINT

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

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

loop 1 &
loop 2 &
wait

Za pomocą pułapki sygnał SIGINT wytwarzany przez CTRL-C jest przechwytywany i zastępowany przez killgroupfunkcję, która zabija wszystkie te procesy.

michas
źródło
Dziękuję za Twoją odpowiedź! Mam jednak pytanie uzupełniające. Mam teraz następujący skrypt: trap killgroup SIGINT killgroup () {echo killing ... kill 0} command001 polecenie1 i polecenie2 Zignorowałem polecenie oczekiwania, ponieważ skrypt może kontynuować po zakończeniu polecenia 2. Wydaje się jednak, że polecenie 1 zaczyna się już po uruchomieniu skryptu. Czy mogę to naprawić? Dzięki
maero21
polecenie1 uruchamia się bezpośrednio po zakończeniu polecenia 001. możesz użyć set -xna początku skryptu, aby wydrukować wykonane polecenia.
michas
6

Podczas wstawiania polecenia w tle ze skryptu PID nie jest wyświetlany na ekranie. Możesz użyć wbudowanej zmiennej, $!która przechowuje PID ostatniego procesu, dzięki czemu możesz przechwycić PID polecenia1.

command1 &
echo $!

powtórzy PID polecenia1.

Bash zapewnia także wbudowaną pułapkę, której można użyć do zarejestrowania sekwencji poleceń uruchamianych po odebraniu określonych sygnałów. Możesz użyć tego, aby złapać SIGINT i kill polecenie1 przed wyjściem ze skryptu głównego, np

#!/bin/bash

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

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

Teraz, gdy polecenie 2 jest uruchomione, naciśnięcie Ctrl Cspowoduje wysłanie zarówno polecenia 1, jak i polecenia 2 SIGINT.

Społeczność
źródło
Możesz także użyć %nskładni, aby zabić określone zadania w tle w tej powłoce. Zwykle próbujesz kill %1zabić najnowszy i jedyny proces w tle. W przypadku większej liczby procesów w tle użyj jobsnajpierw , aby wyświetlić listę.
9000
@ 9000: Tak, ale OP uruchamia swoje polecenia w skrypcie (./execute.sh), więc uzyskanie zadań do pracy jest znacznie bardziej problematyczne?
@lain: na pewno. Brakowało mi punktu na temat uruchamiania ze skryptu.
9000
5

Nowsze wersje GNU Parallel zrobią to, co chcesz:

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

Lub jeśli commandpozostaje taki sam:

parallel command ::: 1 2
echo done

Obejrzyj film wprowadzający, aby zapoznać się z krótkim wprowadzeniem: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

Przejdź przez samouczek (samouczek_ równoległy). Wiersz poleceń z miłością za to.

Ole Tange
źródło
1

Ctrl+ Cwysyła sygnał SIGINT do procesu frontowego, który jest command2. command1jest uruchamiany w tle, dlatego strumień wejściowy go nie dotyczy.

Kiedy command1 &piszesz, bash powinien dać ci proces PID, coś w rodzaju [1234]. Aby zabić ten proces, możesz użyć kill 1234. Teraz, jeśli masz PID obu command1i command2(spójrz na ps -ef), możesz użyć, killaby zakończyć je wszystkie:

kill pid1 pid2 pid3 ...

Mała sztuczka polega na uruchomieniu obu poleceń w tle, z:

command1 & command2 &

Bash da ci oba PID, gotowe do zabicia. Inną sztuczką byłoby przywrócenie command1na pierwszy plan po zabiciu command2:

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

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

Więcej informacji dostępnych tutaj: http://linuxg.net/how-to-manage-background-and-foreground-processes/

John WH Smith
źródło