Jeśli którykolwiek spawnowany proces potomny zawiedzie, zabij wszystko i wyjdź

9

W swoim skrypcie dzielę zestaw danych na input_aa, input_ab itp. Następnie uruchamiam każdy z nich za pomocą tego samego skryptu Python:

# Execute program on each split file
for part in input_*; do
        python3 $part &
done
wait

Moje pytanie jest dwojakie: w jaki sposób mogę wykryć, że proces Pythona nie powiódł się, a po wykryciu, jak zabić wszystkie odrodzone dzieci i wyjść ze skryptu z błędem?

Zdarzać się
źródło

Odpowiedzi:

10

Możesz użyć grupy procesów:

set -m
(
   for part in input_*; do
     (python3 "$part" || kill 0) &
   done
   wait
)

set -m(i opcjonalna funkcja powłoki POSIX, wymagana funkcja powłoki Unix) uruchamia zadania we własnej grupie procesów. W bash, yash, zsh, mksh, to miejsca pracy w podpowłoce gdzie set -mjest włączona więc zewnętrzna (...)i wszystkie procesy utworzone wewnątrz, które zostaną umieszczone w tej samej grupie procesowej.

Dla dashi innych ashpowłok opartych, które działają tylko w procesie powłoki najwyższego poziomu. Więc ten kod będzie działał, chyba że zostanie umieszczony w podpowłoce.

To nie zadziała w AT&T kshani starej powłoce SysV / Bourne.

kill 0 wysyła sygnał SIGTERM do wszystkich członków bieżącej grupy procesów.

Stéphane Chazelas
źródło
W bash Dlaczego zawarłem shebang - wymagana powłoka nie jest jasna. Dobra odpowiedź
Jim Mcnamara,
@jimmcnamara, że prace w bash, dash, yash, mksh, zsh. Zasadniczo każda powłoka POSIX oprócz AT&T ksh. set -mjest (niedostatecznie) określony w POSIX, ale jako funkcja opcjonalna.
Stéphane Chazelas
Używam Solaris. / bin / sh nie będzie latał.
Jim Mcnamara,
@jimmcnamara, no / bin / sh na Solarisie 10 i wcześniejszym to powłoka Bourne'a (nie powłoka POSIX), a na 11, AT&T ksh. Jak powiedziałem, działa w bash, dash, yash, mksh, zsh.
Stéphane Chazelas
1
@mikeserv, co spowodowałoby ponowne odtworzenie procesu do 1, ale nie wyjęcie go z grupy procesów. kill 0zabija wszystkich członków grupy procesowej, niezależnie od tego, kto jest ich rodzicem. Zobacz, ps -jaby zobaczyć identyfikatory grup procesów.
Stéphane Chazelas
3

To jest przykład. GRAJ z tym pierwszym, aby uzyskać dokładnie to, czego potrzebujesz. Nie może złamać tak jak jest.

#!/bin/bash
# Example of killing off all children

> killfile
> outfile.err
kill_em()
{
   echo 'killing all children ' > 2
   while read pid
   do
      kill -0 $pid && kill -9 $pid  # if still running kill it
   done < killfile
   exit 1
}

export grandparentpid=$$
trap 'kill_em' 6
for i in 2 2 3 4 5 6 7 8 9 10
do
        ( sleep $i && ls oinkle  >> outfile 2>> outfile.err &
          pid=$!
          echo $pid >> killfile
          wait $!
          [ $? -ne 0 ] && kill -6 $grandparentpid
        ) &
done
wait

Jest to ustawione na celowe niepowodzenie, ponieważ ls oinklezawiedzie (na moim komputerze).

Kiedy dostaniesz to, czego potrzebujesz po majstrowaniu przy skrypcie startowym --- Zmień:

for i in 2 2 3 4 5 6 7 8 9 10

do:

for part in input_* 

zmiana:

sleep $i && ls oinkle 

do:

python3 $part 

Przekierowania służą do zapisywania dzienników. Możesz ich nie chcieć.

Jim Mcnamara
źródło
To trochę ryzykowne. Jeśli jedno z zadań nie powiedzie się, zanim wszystkie pozostałe zostaną uruchomione, killfilemoże nie zawierać wszystkich pidów uruchomionych zadań.
Stéphane Chazelas
Kilka złych praktyk, takich jak: niecytowane zmienne, użycie numerów sygnałów zamiast nazw, użycie sygnału 6 (na przykład ABRT na Linuksie amd64) zamiast USR1 / USR2 jako sygnału użytkownika, [ $? -ne 0 ]...
Stéphane Chazelas