如何后台运行多个dd命令并查看各自状态?
多dd命令后台运行并对应显示进度的实现方案
问题需求
需要后台运行多个dd擦盘命令,同时让每个命令的进度输出对应显示在其磁盘路径提示的下方,而非多个进度混在一起。
原实现脚本(script.sh)
#!/usr/bin/env bash for drive in $@ do echo "Wiping $drive" dd if=/dev/zero of=$drive status=progress & done wait echo "Done."
原执行输出(进度混叠)
$ sudo bash ./script.sh /dev/sda /dev/sdb Wiping /dev/sda Wiping /dev/sdb 288788992 bytes (289 MB, 275 MiB) copied, 10 s, 28.9 MB/s 14404864 bytes (114 MB, 109 MiB) copied, 4 s, 28.6 MB/s
期望输出(进度对应磁盘)
$ sudo bash ./script.sh /dev/sda /dev/sdb Wiping /dev/sda 288788992 bytes (289 MB, 275 MiB) copied, 10 s, 28.9 MB/s Wiping /dev/sdb 14404864 bytes (114 MB, 109 MiB) copied, 4 s, 28.6 MB/s
尝试过程中的问题
尝试用coproc(协程)实现,但循环中动态命名协程时无法正确获取第二个协程的PID:
硬编码协程(可行)
coproc dd_sda { dd if=/dev/zero of=/dev/sda status=progress 2>&1; } echo "sda PID: $dd_sda_PID" coproc dd_sdb { dd if=/dev/zero of=/dev/sdb status=progress 2>&1; } echo "sdb PID: $dd_sdb_PID"
输出结果
sda PID: 12494 ./wipe.sh: line 86: warning: execute_coproc: coproc [12494:dd_sda] still exists sdb PID: 12496
循环动态命名协程(失败)
for drive in sda sdb do coproc_name=dd_${drive} coproc $coproc_name { dd if=/dev/zero of=/dev/$drive status=progress 2>&1; } pid_var="${coproc_name}_PID" echo "$drive PID: ${!pid_var}" done
输出结果
sda PID: 12759 ./wipe.sh: line 39: warning: execute_coproc: coproc [12759:dd_sda] still exists sdb PID:
硬编码判断的循环协程(可行,但扩展性差)
for drive in sda sdb do coproc_name=dd_${drive} if [[ "$drive" == 'sda' ]] then coproc dd_sda { dd if=/dev/zero of=/dev/$drive status=progress 2>&1; } elif [[ "$drive" == 'sdb' ]] then coproc dd_sdb { dd if=/dev/zero of=/dev/$drive status=progress 2>&1; } fi pid_var="${coproc_name}_PID" echo "$drive PID: ${!pid_var}" done
输出结果
sda PID: 12998 ./wipe.sh: line 39: warning: execute_coproc: coproc [12998:dd_sda] still exists sdb PID: 13000
解决方案
以下两种方法可实现动态批量处理,并确保进度输出与磁盘一一对应:
方法一:动态协程+间接引用
利用eval处理协程的动态命名,同时单独读取每个协程的输出流:
#!/usr/bin/env bash declare -A coproc_pids for drive in "$@" do echo "Wiping $drive" # 替换路径中的/为_,避免非法变量名 coproc_name="dd_${drive//\//_}" # 通过eval创建协程并捕获PID eval "coproc $coproc_name { dd if=/dev/zero of=\"$drive\" status=progress 2>&1; }" pid_var="${coproc_name}_PID" coproc_pids["$drive"]="${!pid_var}" # 后台读取协程输出,确保进度对应磁盘提示 while IFS= read -r line; do echo "$line" done <&"${!coproc_name[0]}" & done wait echo "Done."
方法二:命名管道隔离输出流
为每个磁盘创建独立的命名管道,分离dd的输出:
#!/usr/bin/env bash # 创建临时目录存储管道,脚本退出时自动清理 temp_dir=$(mktemp -d) trap 'rm -rf "$temp_dir"' EXIT for drive in "$@" do # 生成唯一管道名 pipe="$temp_dir/pipe_${drive//\//_}" mkfifo "$pipe" echo "Wiping $drive" # dd后台运行,输出重定向到管道 dd if=/dev/zero of="$drive" status=progress 2>&1 >"$pipe" & # 后台读取管道内容并输出 while IFS= read -r line; do echo "$line" done <"$pipe" & done wait echo "Done."
方案说明
- 方法一通过动态协程+文件描述符控制,直接关联
dd进程的输出流,无需额外文件。 - 方法二用命名管道物理隔离每个
dd的输出,逻辑更直观,兼容性更好。
内容的提问来源于stack exchange,提问作者krystof.k
相关产品推荐
相关产品推荐

