You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何后台运行多个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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 20:15:44