2016-08-06 10 views
1

Я пишу сценарий bash, в котором я написал обработчик, чтобы позаботиться о том, когда пользователь нажал Control + C (используя trap interruptHandler SIGINT), но SIGINT отправляется как в bash скрипт и дочерний процесс, который в настоящее время запущен, закрывая дочерний процесс. Как я могу предотвратить это?Предотвращать закрытие дочернего процесса SIGINT в сценарии bash

редактировать: вот сценарий, не критиковать мои навыки слишком много ..

#!/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 

(Это скрипт для кодирования Matroska (MKV) файлов H.265 в случае, если вам интересно)

+0

В качестве альтернативы, утомительная последовательность 'if [-e $ {cachedir}] может быть заменена просто на' mkdir -p '$ cachedir/out ". Если все каталоги существуют, он ничего не делает; если один или несколько компонентов пути отсутствуют, он создает их все. Обратите внимание также, как скобки являются излишними, но для правильного управления именами каталогов, которые содержат пробелы или ряд других проблемных символов, необходимы двойные кавычки. – tripleee

ответ

1

Выполненный простой тест здесь, и это обеспечивает ожидаемый результат:

int.sh содержимое:

#!/bin/bash 

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

Тестирование:

$ ./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 (отвечая на вопрос редактирование):

Вы, вероятно, хотите, чтобы заманить в ловушку и игнорировать SIGINT для любой другой команды, такие как (trap '' SIGINT && command) в сценарии, так что вы можете предотвратить сигнал, выведенный из текущей команды до того, как вызывается interruptHandler.

Простой пример того, что происходит:

#!/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 

Выход:

$ 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 

Обратите внимание, что это продолжалось только в течение 0,6 секунд из-за SIGINT поимки.

Но когда вы игнорируете SIGINT для sleep:

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

trap intHandler SIGINT 

(trap '' SIGINT && sleep 5) 

Выход есть:

$ 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 

Обратите внимание, что, несмотря на SIGINT был доставлен и захватывается скрипте intHandler будет возвращать только когда текущий sleep выходов, а также обратите внимание, что текущий sleep не поймал SIGINT от родителя (он продолжался в течение 5 секунд) в качестве подоболочки где он работает ((...)) игнорирует SIGINT.

+0

Пожалуйста, взгляните на скрипт. –

+0

@JohnLeuenhagen Если вы не игнорируете SIGINT, и вы улавливаете его с помощью специального обработчика, обратите внимание, что текущая команда поймает сигнал, и bash войдет в обработчик и вернется только после выхода текущей команды. Если вы хотите обрабатывать SIGINT, а также предоставить, чтобы он игнорировался во всех дочерних процессах, вам нужно сделать это явно, например '(trap '' SIGINT && rm $ {cachedir}/*. Mkv)' для каждой команды. – pah

1

Сигнал отправляется на все задания в текущем процессе переднего плана. Поэтому самый простой способ предотвратить передачу сигнала ребенку - это вывести его из переднего плана.Просто фон FFmpeg вызов, выполнив:

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

Обратите внимание, что это также дает ИДП ребенка более энергично, что пытается разобрать вывод ps, так что вы можете сделать:

ffmpeg ... & 
ffmpegPID=$! 
wait 
0

Взгляните на это:

#!/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 

Пояснения:

  • Ребенок ffmpeg (sleep здесь) должен игнорировать SIGINT. Для этого запустите его с bash -c, сбросьте обработчик, затем exec. Достаточно вывести ребенка из переднего плана, чтобы он не получал SIGINT.

  • В материнской плате простой wait не будет по причинам, описанным here. (Попробуйте.) В этом случае родитель продолжит выполнение своего обработчика SIGINT, но до того, как будет выполнен ребенок. Вместо этого мы используем цикл и ожидаем, используя дочерний pid.

  • После законного выхода ребенка еще один kill будет выполнен на несуществующем pid, чей stderr мы игнорируем.

+0

Это позволило обработчику запускаться, а процесс был заморожен, но похоже, что SIGINT все еще проходит через ffmpeg после завершения обработчика. –