Bash后台进程错误检测疑问:为何error变量未正确递增
问题:Bash脚本中error变量为何始终为0?
我编写了如下Bash脚本:
error = 0 do_background() { command $1 || { echo "command has failed with status $?" ((error++)); } } for i in 1..20; do do_background $i & done wait if [[ $error != 0 ]]; then echo "Detected $error errors" exit 1 fi
使用-x选项运行后得到如下输出:
+ command 1 + echo 'command has failed with status 1' command has failed with status 1 + (( error++ )) + [[ 0 != 0 ]]
我的问题是:为何error变量的值没有大于0?
原因分析
后台进程的变量隔离
你用&把do_background放到后台运行时,每个后台进程都是当前Shell的子进程。子进程会复制父进程的变量,但子进程对变量的修改不会同步回父进程。哪怕后台里((error++))执行了,父进程里的error还是初始的0值。额外的语法错误
脚本第一行error = 0的写法不对,Bash变量赋值不能在=两边加空格,正确写法是error=0。不过这不是error不增长的核心原因,但会触发error: command not found的报错。
另外原脚本的for i in 1..20是错误写法,Bash里这样会把1..20当成单个字符串,无法实现1到20的循环,正确写法是for i in {1..20}。
解决办法
要统计后台进程的错误数量,得绕过子进程变量隔离的问题,这里提供两种可行思路:
思路一:用文件存储错误计数
让后台进程把错误数写入文件,父进程最后读取文件内容统计,用文件锁避免多进程写入冲突:
# 初始化错误计数文件 echo 0 > error_count.txt do_background() { command $1 || { echo "command has failed with status $?" # 原子性增加计数 flock -x error_count.txt sh -c 'echo $(( $(cat error_count.txt) + 1 )) > error_count.txt' } } for i in {1..20}; do do_background $i & done wait error=$(cat error_count.txt) if [[ $error != 0 ]]; then echo "Detected $error errors" rm error_count.txt exit 1 fi rm error_count.txt
思路二:捕获子进程退出状态
保存所有后台进程的PID,逐个等待并统计非0退出状态的数量:
error=0 do_background() { command $1 } # 保存所有后台进程PID pids=() for i in {1..20}; do do_background $i & pids+=($!) done # 逐个等待并统计错误 for pid in "${pids[@]}"; do wait $pid if [[ $? -ne 0 ]]; then ((error++)) fi done if [[ $error != 0 ]]; then echo "Detected $error errors" exit 1 fi
内容的提问来源于stack exchange,提问作者david.perez
相关产品推荐
相关产品推荐

