2016-08-06 32 views
1

Ich schreibe ein Bash-Skript, in dem ich einen Handler geschrieben habe, der darauf achtet, wenn der Benutzer Strg + C drückt (mit trap interruptHandler SIGINT), aber das SIGINT wird an beide Bash gesendet Skript und der untergeordnete Prozess, der gerade ausgeführt wird, schließen den untergeordneten Prozess. Wie kann ich das verhindern?Verhindern, dass SIGINT den untergeordneten Prozess im Bash-Skript schließt

edit: hier ist das Skript, nicht meine Fähigkeiten nicht zu viel Kritik ..

#!/bin/bash 
trap "interruptHandler" SIGINT 

inInterrupt=false; 
quit=false; 

if [ -z ${cachedir+x} ]; then cachedir=~/.cache/zlima12.encoding; fi 
cachedir=$(realpath ${cachedir}); 


if [ ! -e ${cachedir} ]; then mkdir ${cachedir}; fi 
if [ ! -e ${cachedir}/out ]; then mkdir ${cachedir}/out; fi 

cleanCache() 
{ 
    rm ${cachedir}/*.mkv; 
    rm ${cachedir}/out/*.mkv; 
} 

interruptHandler() 
{ 
    if [ ${inInterrupt} != true ]; then 
     printf "BASHPID: ${BASHPID}"; 
     inInterrupt=true; 
     ffmpegPID=$(pgrep -P ${BASHPID}); 
     kill -s SIGTSTP ${ffmpegPID}; 
     printf "\nWould you like to quit now(1) or allow the current file to be encoded(2)? "; 
     read response; 
     if [ ${response} = "1" ]; then kill ${ffmpegPID}; cleanCache; 
     elif [ ${response} = "2" ]; then quit=true; kill -s SIGCONT ${ffmpegPID}; 
     else printf "I'm not sure what you said... continuing execution.\n"; kill -s SIGCONT ${ffmpegPID}; 
     fi 

     inInterrupt=false; 
    fi 
} 



for param in "[email protected]"; do 

    dir=$(realpath ${param}); 

    if [ ! -e ${dir} ]; then 
     printf "Directory ${dir} doesn't seem to exist... Exiting...\n" 
     exit 1; 
    elif [ -e ${dir}/new ]; then 
     printf "${dir}/new already exists! Proceed? (y/n) "; 
     read response; 
     if [ ${response} != y ]; then exit 1; fi 
    else 
     mkdir ${dir}/new; 
    fi 

    for file in ${dir}/*.mkv; do 
     filename="$(basename ${file})"; 
     cp $file ${cachedir}/${filename}; 
     ffmpeg -vsync passthrough -i ${cachedir}/${filename} -c:v libx265 -c:a copy -f matroska ${cachedir}/out/${filename}; 
     rm ${cachedir}/${filename}; 
     mv ${cachedir}/out/${filename} ${dir}/new/${filename}; 

     if [ ${quit} = true ]; then exit 0; fi 
    done 
done 

(Dies ist ein Skript matroska (MKV) -Dateien in H.265, falls Sie neugierig zu kodieren)

+0

Als beiseite, die langweilige 'if [-e $ {cachedir}]' Sequenz ersetzt werden kann, einfach mit 'mkdir -p "$ cachedir/out"'. Wenn alle Verzeichnisse vorhanden sind, tut es nichts; Wenn eine oder mehrere Pfadkomponenten fehlen, werden alle erstellt. Beachten Sie auch, wie die geschweiften Klammern völlig überflüssig sind, aber doppelte Anführungszeichen sind notwendig, um korrekt mit Verzeichnisnamen fertig zu werden, die Leerzeichen oder eine Anzahl anderer problematischer Zeichen enthalten. – tripleee

Antwort

1

Performed einen einfachen Test hier und liefert das erwartete Ergebnis:

int.sh Inhalt:

#!/bin/bash 

trap '' SIGINT 
tail -f /var/log/syslog >& /dev/null 

Testing:

$ ./int.sh 
^C^C 
# ... SIGINT ignored (CTRL+C) ... 
# ... Will send SIGTSTP with CTRL+Z ... 
^Z 
[1]+ Stopped     ./int.sh 
$ kill %1 
$ 
[1]+ Terminated    ./int.sh 
$ 

EDIT (die Frage bearbeiten zu beantworten):

Sie wahrscheinlich zu stoppen wollen und SIGINT für jeden anderen Befehl, wie (trap '' SIGINT && command) in Ihrem Skript ignorieren, so dass Sie das Signal Wesen verhindern können wird vom aktuellen Befehl abgefangen, bevor interruptHandler aufgerufen wird.

Ein einfaches Beispiel, was passiert:

#!/bin/bash 

function intHandler() { 
     echo "If SIGINT was caught, this will be printed AFTER sleep exits." 
} 

trap intHandler SIGINT 

sleep 5 # Sleep will exit as soon as SIGINT is caught 

Ausgang:

$ time ./int.sh 
^C 
# ... Right here, only 0.6 seconds have elapsed before the below message being printed ... 
If SIGINT was caught, this will be printed AFTER sleep exits. 

real 0m0.634s 
user 0m0.004s 
sys  0m0.000s 

Beachten Sie, dass es nur für 0,6 Sekunden dauerte aufgrund SIGINT gefangen.

Aber wenn man SIGINT ignorieren für sleep:

function intHandler() { 
     echo "If SIGINT was caught, this will be printed AFTER sleep exits." 
} 

trap intHandler SIGINT 

(trap '' SIGINT && sleep 5) 

Die Ausgabe lautet:

$ time ./int.sh 
^C 
# ... Right here, 5 seconds have elapsed without any message ... 
If SIGINT was caught, this will be printed AFTER sleep exits. 

real 0m5.007s 
user 0m0.000s 
sys  0m0.000s 

anzumerken, dass trotz der SIGINT wurde durch das Skript geliefert und gefangen, wird die intHandler nur zurück, wenn die Aktuelle sleep Exits, und beachten Sie auch, dass die aktuelle sleep nicht die SIGINT von den Eltern gefangen (dauerte es für die volle 5 Sekunden) als Subshell wo es läuft ((...)) ignoriert SIGINT.

+0

Bitte schauen Sie sich das Skript an. –

+0

@JohnLeuenhagen Wenn Sie SIGINT nicht ignorieren und es mit einem benutzerdefinierten Handler abfangen, beachten Sie, dass der aktuelle Befehl das Signal abfängt und bash in den Handler eingeht und nur zurückkehrt, wenn der aktuelle Befehl beendet wird. Wenn Sie mit SIGINT arbeiten und auch sicherstellen möchten, dass es in allen untergeordneten Prozessen ignoriert wird, müssen Sie dies explizit für jeden Befehl tun, z. B. '(trap '' SIGINT && rm $ {cachedir}/*. Mkv)'. – pah

1

Das Signal wird an alle Jobs im aktuellen Vordergrundprozess gesendet. Der einfachste Weg, zu verhindern, dass das Signal zum Kind gelangt, besteht darin, es aus dem Vordergrund zu bekommen.Gerade Hintergrund des ffmpeg Anruf, indem Sie:

... 
ffmpeg -vsync passthrough -i ${cachedir}/${filename} -c:v libx265 -c:a copy -f matroska ${cachedir}/out/${filename} & 
wait 
... 

Beachten Sie, dass dies gibt Ihnen auch die pid des Kindes robusten, dass die Ausgabe von ps zu analysieren versucht, so was Sie tun mögen:

ffmpeg ... & 
ffmpegPID=$! 
wait 
0

Werfen Sie einen Blick auf diese:

#!/bin/bash 
echo $$ 
trap 'echo "got C-c"' SIGINT 
#bash -c 'trap - SIGINT; echo $$; exec sleep 60' & 
sleep 60 & 
pid=$! 
echo "$$: waiting on $pid" 
while kill -0 $pid 2>/dev/null; do 
     wait $pid 
done 
echo done 

Erläuterungen:

  • Das Kind ffmpeg (sleep hier) muss SIGINT selbst ignorieren. Um dies zu tun, starte es mit bash -c, setze den Handler zurück, dann exec. Es reicht aus, das Kind aus dem Vordergrund zu holen, um zu verhindern, dass es SIGINT empfängt.

  • In der Eltern, eine einfache wait wird nicht tun, aus Gründen here erklärt. (Probieren Sie es aus.) In diesem Fall würde das übergeordnete Element nach der Ausführung seines SIGINT-Handlers fortgesetzt, aber noch bevor das Kind fertig ist. Stattdessen verwenden wir eine Schleife und warten mit der untergeordneten PID.

  • Nach dem legitimen Beenden des Kindes wird ein weiterer kill auf einer nicht existierenden PID ausgeführt, deren stderr wir ignorieren.

+0

Dadurch konnte der Handler ausgeführt und der Prozess eingefroren werden. Es scheint jedoch so, als ob das SIGINT nach Abschluss des Handlers weiterhin zu ffmpeg übergeht. –