如何在子shell中检测远程文件缺失或磁盘满时退出并行拷贝脚本?
解决子shell错误时退出整个脚本的问题
首先,你当前脚本里用括号把每个scp命令包裹起来,这会创建独立的子shell——子shell里的exit只能退出自身,无法终止父脚本。要实现远程文件不存在/磁盘满时退出整个脚本,可以从以下几个方面调整:
1. 移除不必要的子shell,保留退出码传递
把每个scp外的括号去掉,直接让命令在当前shell环境执行,这样scp的错误码会直接传递给do_copy函数。修改后的do_copy函数如下:
do_copy() { el=$1 primsec=$2 # 尝试第一个节点拷贝,失败则试第二个,再失败试第三个 scp -C -o StrictHostKeyChecking=no \ goldy@"$remote":"$pod1"/abc_187_"$el"_111_8.data "$primsec"/. > /dev/null 2>&1 \ || scp -C -o StrictHostKeyChecking=no \ goldy@"$remote":"$pod2"/abc_187_"$el"_111_8.data "$primsec"/. > /dev/null 2>&1 \ || scp -C -o StrictHostKeyChecking=no \ goldy@"$remote":"$pod3"/abc_187_"$el"_111_8.data "$primsec"/. > /dev/null 2>&1 }
这样,如果三个scp都失败(不管是远程文件不存在还是本地磁盘满),do_copy函数会返回非0的退出码。
2. 让父脚本感知错误并退出
情况A:串行执行拷贝
如果是逐个执行do_copy,可以在调用时直接检查退出码,失败就终止整个脚本:
# 示例:循环调用do_copy,失败则退出 for el in "${elements[@]}"; do do_copy "$el" "$primary" || { echo "拷贝$el失败,退出脚本"; exit 1; } done
情况B:并行执行拷贝
如果是后台并行执行(比如加&),父脚本默认不会等待子进程,也不会感知子进程的错误。这时候可以用以下方法:
- 记录所有子进程的PID,然后用
wait逐个检查退出码,发现错误就终止脚本:
# 存储子进程PID的数组 pids=() for el in "${elements[@]}"; do do_copy "$el" "$primary" & pids+=($!) done # 等待所有子进程完成,检查退出码 for pid in "${pids[@]}"; do wait "$pid" if [ $? -ne 0 ]; then echo "有拷贝任务失败,终止脚本" # 杀死其他正在运行的子进程 kill "${pids[@]}" 2>/dev/null exit 1 fi done
这样只要有一个并行的拷贝任务失败,父脚本就会终止所有子进程并退出。
3. 增强脚本的错误感知(可选)
在脚本开头添加set -eo pipefail,可以让脚本在命令失败、管道错误时自动退出,进一步强化错误处理:
#!/bin/bash set -eo pipefail # 命令失败/管道错误时自动退出脚本 primary=/bat/primary secondary=/bat/secondary # ... 后续代码 ...
注意:如果用了这个选项,并行执行时需要结合上面的PID等待逻辑,因为set -e不会自动处理后台进程的错误。
验证错误场景
- 远程文件不存在:
scp会返回非0退出码,触发do_copy失败,进而让父脚本退出。 - 本地磁盘已满:
scp写入本地文件失败时也会返回非0退出码,同样会触发脚本退出。
内容的提问来源于stack exchange,提问作者flash
相关产品推荐
相关产品推荐

