
Я придумал настройку, которая, как я думаю, это сделает, но она не работает:
#!/bin/bash
echo "Launching a background process that may take hours to finish.."
myprog &
pid=$!
retval=
##At this time pid should hold the process id of myprog
echo "pid=${pid}"
{
##check if the process is still running
psl=$(ps -f -p ${pid} | grep -E "\bmyprog\b")
killit=
while [[ ! -z ${psl} ]]
do
##if a file named "kill_flag" is detected, kill the process
if [[ -e "kill_flag" ]]
then
killit=YES
break
fi
#check every 3 seconds
sleep 3
psl=$(ps -f -p ${pid} | grep -E "\bmyprog\b")
done
##killit not set, normal exit, read from fd5
if [[ -z ${killit} ]]
then
read <&5 retval
else
##kill here, the wait will return and the sub process ends
kill ${pid}
fi
} 5< <( wait ${pid} > /dev/null 2>&1; echo $? )
echo "retval=$retval"
При первом запуске все кажется в порядке, я могу убить процесс с помощью touch kill_flag
, в противном случае он ждет, пока myprog нормально завершит работу. Но потом я заметил, что всегда получаю -1 в retval. myprog возвращает 0, что подтверждается нормальным запуском. Дальнейшее расследование показало, что echo $?
часть " " была выполнена сразу после запуска скрипта, а не после завершения команды wait. Мне интересно, что здесь происходит. Я новичок в bash.
решение1
wait
может работать только с потомками текущего процесса оболочки. Подоболочка, которая интерпретирует код внутри, <(...)
не может ждать родственный процесс.
Ожидание должно быть выполнено тем же процессом оболочки, который запустил pid. Вместо zsh
этого bash
(здесь предполагается, что нет других запущенных фоновых заданий):
cmd & pid=$!
while (($#jobstates)) {
[[ -e killfile ]] && kill $pid
sleep 3
}
wait $pid; echo $?
решение2
Придумал работоспособную версию:
#!/bin/bash
export retval=
##At this time pid should hold the process id of myprog
{
##This is the subshell that launched and monitoring myprog
subsh=$!
##Since myprog is probably the only child process of this subsh, it should be pretty safe
pid=$(ps -f --ppid ${subsh} | grep -E "\bmyprog\b" | gawk '{print $2}' )
##check if the process is still running
psl=$(ps -f -p ${pid} | grep -E "\bmyprog\b")
killit=
while [[ ! -z ${psl} ]]
do
##if a file named "kill_flag" is detected, kill the process
if [[ -e "kill_flag" ]]
then
killit=YES
break
fi
#check every 3 seconds
sleep 3
psl=$(ps -f -p ${pid} | grep -E "\bmyprog\b")
done
##killit not set, normal exit, read from fd5
if [[ -z ${killit} ]]
then
read <&5 retval
else
##kill here, the wait will return and the sub process ends
kill ${pid}
fi
} 5< <( myprog >>logfile 2>&1; echo $? )
echo "retval=$retval"
Единственное, что раздражает, это то, что когда я завершаю myprog с помощью семафора, возникает ошибка, поскольку подстановка процесса завершена, но ее можно легко перехватить.