Uruchom wiele poleceń i zabij je jako jedno w bash


51

Chcę uruchomić wiele poleceń (procesów) na jednej powłoce. Wszystkie z nich mają swoją ciągłą wydajność i nie przestawają. Uruchamiając je w przerwach tła Ctrl- C. Chciałbym uruchomić je jako pojedynczy proces (może podpowłoka?), Aby móc zatrzymać je wszystkie za pomocą Ctrl- C.

Mówiąc ściślej, chcę uruchomić testy jednostkowe w mocha(tryb oglądania), uruchomić serwer i uruchomić wstępne przetwarzanie plików (tryb oglądania) i zobaczyć dane wyjściowe każdego z nich w jednym oknie terminala. Zasadniczo chcę uniknąć używania programu uruchamiającego zadania.

Mogę to zrealizować, uruchamiając procesy w tle ( &), ale potem muszę je umieścić na pierwszym planie, aby je zatrzymać. Chciałbym mieć proces ich owijania, a kiedy zatrzymam proces, zatrzyma to jego „dzieci”.


Czy powinny biec równolegle, czy jeden po drugim?
Minix

Tak, procesy powinny działać równolegle, ale muszę zobaczyć dane wyjściowe z każdego z nich.
user1876909

Odpowiedzi:


67

Aby uruchamiać polecenia jednocześnie, możesz użyć &separatora poleceń.

~$ command1 & command2 & command3

Rozpocznie się command1, a następnie uruchomi w tle. To samo z command2. Potem zaczyna się command3normalnie.

Dane wyjściowe wszystkich poleceń będą zniekształcone, ale jeśli nie będzie to dla ciebie problemem, to będzie rozwiązanie.

Jeśli chcesz osobno spojrzeć na dane wyjściowe później, możesz potokować dane wyjściowe każdego polecenia do tee, co pozwala określić plik, do którego ma być dublowany wynik.

~$ command1 | tee 1.log & command2 | tee 2.log & command3 | tee 3.log

Dane wyjściowe będą prawdopodobnie bardzo nieporządne. Aby temu przeciwdziałać, możesz nadać wyjściu każdego polecenia prefiks sed.

~$ echo 'Output of command 1' | sed -e 's/^/[Command1] /' 
[Command1] Output of command 1

Jeśli więc połączymy to wszystko razem, otrzymamy:

~$ command1 | tee 1.log | sed -e 's/^/[Command1] /' & command2 | tee 2.log | sed -e 's/^/[Command2] /' & command3 | tee 3.log | sed -e 's/^/[Command3] /'
[Command1] Starting command1
[Command2] Starting command2
[Command1] Finished
[Command3] Starting command3

To wysoce wyidealizowana wersja tego, co prawdopodobnie zobaczysz. Ale to najlepsze, o czym mogę teraz myśleć.

Jeśli chcesz zatrzymać je wszystkie naraz, możesz skorzystać z wbudowanej wersji trap.

~$ trap 'kill %1; kill %2' SIGINT
~$ command1 & command2 & command3

Będzie to wykonać command1, a command2w tle i command3w, co pozwala zabić go na pierwszym planie Ctrl+ C.

Po zabiciu ostatniego procesu z Ctrl+ Cte kill %1; kill %2polecenia są wykonywane, ponieważ ich wykonanie połączone z odbiorem sygnału, a INTerupt rzeczy wysłanego przez naciśnięcie Ctrl+ C.

Odpowiednio zabijają pierwszy i drugi proces w tle (twój command1i command2). Nie zapomnij usunąć pułapki po zakończeniu korzystania z poleceń trap - SIGINT.

Ukończ potwora polecenia:

~$ trap 'kill %1; kill %2' SIGINT
~$ command1 | tee 1.log | sed -e 's/^/[Command1] /' & command2 | tee 2.log | sed -e 's/^/[Command2] /' & command3 | tee 3.log | sed -e 's/^/[Command3] /'

Możesz oczywiście spojrzeć na ekran . Pozwala podzielić konsolę na tyle oddzielnych konsol, ile chcesz. Możesz więc monitorować wszystkie polecenia osobno, ale jednocześnie.


3
Czekam na całą krytykę. :)
Minix

2
Dziękuję Ci. Robi dokładnie to, co chciałem (polecenie pułapki). Rozwiązanie musi być jednak zawarte w skrypcie umożliwiającym ponowne użycie.
user1876909

@ user1876909 Oto szkielet. Specyfika zależy od Ciebie [1]. Cieszę się, że mogłem pomóc. [1] pastebin.com/jKhtwF40
Minix

3
to dość sprytne rozwiązanie, iv dowiedziałem się czegoś nowego, brawo +1
mike-m

1
To jest niesamowite. Mam wiele do zagłębiania się i uczenia się z tej odpowiedzi.
ccnokes,

20

Możesz łatwo zabić kilka procesów naraz, jeśli zorganizujesz je (i tylko one) w tej samej grupie procesów .

Linux zapewnia narzędzie setsiddo uruchamiania programu w nowej grupie procesów (nawet w nowej sesji, ale nas to nie obchodzi). (Jest to wykonalne, ale bardziej skomplikowane bez setsid.)

Identyfikator grupy procesów (PGID) grupy procesów jest identyfikatorem pierwotnego procesu nadrzędnego w grupie. Aby zabić wszystkie procesy w grupie procesów, przekaż ujemną wartość PGID do killwywołania systemowego lub polecenia. PGID pozostaje ważny, nawet jeśli pierwotny proces z tym PID umrze (choć może to być nieco mylące).

setsid sh -c 'command1 & command2 & command3' &
pgid=$!
echo "Background tasks are running in process group $pgid, kill with kill -TERM -$pgid"

Jeśli uruchomisz procesy w tle z nieinteraktywnej powłoki, wszystkie one pozostaną w grupie procesów powłoki. Tylko w interaktywnych powłokach procesy w tle działają we własnej grupie procesów. Dlatego jeśli rozwidlisz polecenia z nieinteraktywnej powłoki, która pozostaje na pierwszym planie, Ctrl+ Czabije je wszystkie. Użyj waitwbudowanego polecenia, aby powłoka czekała na zakończenie wszystkich poleceń.

sh -c 'command1 & command2 & command3 & wait'
# Press Ctrl+C to kill them all

2
odpowiedź nieprzemijająca (metoda na dole). Prosty do zrozumienia i zapamiętania.
Nicolas Marshall,

14

Dziwię się, że jeszcze go tu nie ma, ale moim ulubionym sposobem jest użycie podpowłoki z poleceniami w tle. Stosowanie:

(command1 & command2 & command3)

Dane wyjściowe są widoczne z obu, wszystkie polecenia bash i udogodnienia są nadal dostępne, a jeden Ctrl-czabija je wszystkie. Zwykle używam tego, aby jednocześnie uruchomić serwer plików klienta do debugowania na żywo i serwer zaplecza, dzięki czemu mogę je łatwo zabić, kiedy chcę.

Działa to w ten sposób command1i command2działają w tle nowej instancji podpowłoki i command3znajdują się na pierwszym planie tej instancji, a podpowłoka jest tym, co najpierw odbiera sygnał zabicia od Ctrl-cnaciśnięcia klawisza. To bardzo przypomina uruchamianie skryptu bash w odniesieniu do tego, kto jest właścicielem danego procesu i kiedy programy w tle zostają zabite.


Jeśli dodasz waitjako ostatnie polecenie (polecenie 3 w twoim przykładzie), możesz wykonać kod czyszczenia po naciśnięciu przez użytkownika Ctrl-c.
dotnetCarpenter

0

Możesz użyć średnika ;lub w &&ten sposób:

cmd1; cmd2   # Runs both the commands, even if the first one exits with a non-zero status.

cmd1 && cmd2 # Only runs the second command if the first one was successful.

To nie uruchamia poleceń jednocześnie.
Gilles „SO- przestań być zły”

-2

Możesz użyć pipe. Spowoduje to uruchomienie poleceń na obu końcach potoku w tym samym czasie. Powinieneś być w stanie zatrzymać wszystkie polecenia za pomocą CTRL-C.

1st command | 2nd command | 3rd command

Jeśli chcesz uruchamiać polecenia jeden po drugim, możesz użyć metody @ serenesat.


Powoduje to przekazanie wyniku pierwszego polecenia do drugiego (i tak dalej), co tutaj nie jest właściwe, ponieważ wyjście pierwszego polecenia ma zakończyć się na terminalu.
Gilles „SO- przestań być zły”
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.