簡體   English   中英

使用后台進程捕獲並退出大 shell 腳本

[英]Trap and exit big shell script with background processes

我有 2 個 shell 腳本,其中包含 ffmpeg 個命令(command1.sh 和 command2.sh)。 command2.sh 有大約 500 個 ffmpeg 命令,這些命令用“;”一個接一個地觸發,而 command1.sh 處理音頻 ffmpeg 命令。

主要問題:殺死整個腳本需要太多時間,它占用了我執行另一個腳本所需的 CPU 能力 1-2 分鍾,所以我白白失去了 CPU 能力,因為我無法立即殺死它。

代碼:我有init.sh包含:

trap 'print TERM received;exit' 15
chmod +x command1.sh;
chmod +x command2.sh;
./command1.sh & ./command2.sh

所以它在后台觸發。

然后我執行pkill init.sh並捕獲 shell陷阱退出shell 腳本但我得到 exitCode: 1, failed: true 並且后台命令仍然執行 1 分鍾,直到它們被另一個 kill ${pid我在 pkill 之后執行。

並行子任務

一些評論:

  • chmod +x在這里沒用。 您完全可以改為運行sh command1.sh &
  • 您必須獨立終止所有子任務
  • 由於這個問題被標記為 ,我的回答不使用bashisms 這里的所有腳本都在下進行了測試。

就像是:

#!/bin/sh

for cmd in ./command1.sh ./command2.sh;do
    exec $cmd &
    PIDS="$PIDS $!"
done

trap "kill $PIDS;exit" 15
wait

當然,在for cmd in;do之間,您可以放置任意數量的commandXX.sh (只要您將行長度保持在已安裝操作系統支持的最大長度)

測試腳本:

這是一個快速的測試腳本,它在 2.0 到 12.99 秒之間隨機休眠,然后打印done. 退出前:

#!/bin/bash

declare -i toSleep
case $1 in '' | *[!0-9]* ) toSleep='RANDOM%10+2' ;; * ) toSleep=$1 ;; esac

exec {dummy}<> <(:)
read -t $toSleep.$RANDOM -u $dummy _
echo done.

我已將其保存到command1.sh , chmod +x..並鏈接到command2.sh ...

更便攜的包裝器:

#!/bin/sh

for cmd in "$@";do
    exec $cmd &
    PIDS="$PIDS $!"
done

printf "You have to: kill -TERM %d\nto end %d tasks: %s\n" \
    $$ $(echo $PIDS|wc -w) "$PIDS"

trap "kill $PIDS;echo 'Process $$ killed.';exit" 15
wait

echo "Process $$ running $@ ended normally"

您可以將此腳本保存到名為simpleParallel.sh的文件中,例如,然后:

chmod +x simpleParallel.sh

./simpleParallel.sh ./command1.sh ./command2.sh 
You have to: kill -TERM 741297
to end 2 tasks:  741298 741299

然后,如果您從其他地方kill -TERM 741297

Process 741297 killed.

但如果你不這樣做,你可能會讀到這樣的東西:

./simpleParallel.sh ./command1.sh ./command2.sh 
You have to: kill -TERM 741968
to end 2 tasks:  741969 741970
done.
done.
Process 741968 running ./command1.sh ./command2.sh ended normally

注意:如果您在一個進程已完成時發送 kill 命令,您可能會看到如下錯誤消息:

./simpleParallel.sh: 1: kill: No such process

版本可以避免這個bug。

從另一個終端控制台跟隨這個

在運行./simpleParallel.sh之前,您可以不帶參數運行tty

tty
/dev/pts/2

然后在一個新的免費 window 中,你可以運行:

watch ps --tty pts/2

同時再次使用另一個 window 運行kill命令。

帶有一些bashisms ,現在:

在最近的bash下,有很多特色。

  • 您可以使用 Array 來存儲后台任務的 PID。
  • 從 5.0 版本開始,有一個$EPOCHREALTIME變量,它以微秒粒度擴展為自 Unix 紀元以來的秒數。
  • 從 5.0 版開始,Associative arrays 允許下標包含空格。
  • 從 5.1 版本開始, wait有了一個新的[-p VARNAME]選項,它存儲了wait -nwait沒有 arguments 返回的PID

我的腳本可能變成:

#!/bin/bash
declare -A PIDS ENDED
started=$EPOCHREALTIME
for cmd; do
    exec "$cmd" &
    PIDS["$cmd"]=$!
    CMDS[$!]="$cmd"
done
printf "You have to: kill -TERM %d\nto end %d tasks: %s\n" \
    $$ ${#PIDS[@]} "${PIDS[*]}"

trapExit(){
    kill "${PIDS[@]}"
    printf 'Process %s + %d task killed: %s\n' $$ ${#PIDS[@]} "${!PIDS[*]}"
    showDone
    exit 1
}
trap trapExit 15
showDone() {
    for cmd in "${!ENDED[@]}";do
        read -r elap < <(bc -l <<<"${ENDED["$cmd"]}-$started")
        printf "Elapsed: %.4f sec for %s\n" "$elap" "$cmd"
    done
}
while ((${#PIDS[@]}));do  if wait -n -p pid ;then
        printf "Process %d done (%s).\n" "$pid" "${CMDS[pid]}"
        ENDED["${CMDS[pid]}"]=$EPOCHREALTIME
        unset PIDS["${CMDS[pid]}"] CMDS[pid]
fi; done
echo "Process $$ running $* ended normally"
showDone

Output 可能看起來像:

./simpleParallel.bash ./command{1,2}.sh
You have to: kill -TERM 1309816
to end 2 tasks: 1309817 1309818
done.
Process 1309817 done (./command1.sh).
done.
Process 1309818 done (./command2.sh).
Process 1309816 running ./command1.sh ./command2.sh ended normally
Elapsed: 3.1644 sec for ./command1.sh
Elapsed: 4.2488 sec for ./command2.sh

或者

./simpleParallel.bash ./command{1,2}.sh
You have to: kill -TERM 1310031
to end 2 tasks: 1310032 1310033
done.
Process 1310033 done (./command2.sh).
done.
Process 1310032 done (./command1.sh).
Process 1310031 running ./command1.sh ./command2.sh ended normally
Elapsed: 9.1868 sec for ./command1.sh
Elapsed: 3.2310 sec for ./command2.sh

如果你早點kill了他們:

./simpleParallel.bash ./command{1,2}.sh
You have to: kill -TERM 1294577
to end 2 tasks: 1294578 1294579
Process 1294577 + 2 task killed: ./command1.sh ./command2.sh

如果你稍后kill了他們:

./simpleParallel.bash ./command{1,2}.sh
You have to: kill -TERM 1294958
to end 2 tasks: 1294959 1294960
done.
Process 1294959 done (./command1.sh).
Process 1294958 + 1 task killed: ./command2.sh
Elapsed: 3.1779 sec for ./command1.sh

或者

./simpleParallel.bash ./command{1,2}.sh
You have to: kill -TERM 1294971
to end 2 tasks: 1294972 1294973
done.
Process 1294973 done (./command2.sh).
Process 1294971 + 1 task killed: ./command1.sh
Elapsed: 6.9344 sec for ./command2.sh

並且沒有關於試圖殺死不存在的 pid 的錯誤消息。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM