shell 如何在后台运行多个dd命令并查看状态?

nwlqm0z1  于 2023-01-05  发布在  Shell
关注(0)|答案(2)|浏览(397)

我想在后台运行多个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

是否有办法在驱动器路径下输出相应的dd状态?例如:

$ 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

我尝试了各种重定向,命名管道等,但无法实现这样(或类似)的输出。

rwqw0loc

rwqw0loc1#

将每个dd的状态重定向到一个文件,并在清除旧输出的同时重复打印这些文件。
GNU coreutils的dd status=progress将其状态打印到stderr,因此使用2>重定向状态信息。
状态的更新是用\r覆盖当前行,然后是新的状态。由于\r只适用于单行,更新四个不同的行需要终端控制序列,例如ANSI转义码,可以使用cleartput等命令方便地打印出来。

outfile_prefix=/tmp/wipe-status-$$-
for drive in "$@"; do
  (
    echo "Wiping $drive"
    # dummy-version for testing ...
    dd if=/dev/zero of=/dev/null bs=1 count=5M status=progress 2>&1
    # ... if you are happy with the output, replace it with
    # dd if=/dev/zero of="$drive" status=progress 2>&1
  ) > "$outfile_prefix$BASHPID" &
done

clear -x  # clear the currently visible screen
while             # do-while loop
  tput home                  # move cursor to top left
  sed '' "$outfile_prefix"*  # print files like `cat`, but with \n at the end
  jobs %% &> /dev/null       # while jobs are running
do
  sleep 1
done

rm "$outfile_prefix"*

如果您正在擦除许多大的或慢的驱动器,上面的代码是低效的,因为状态文件不断增长(即使它们 * 似乎 * 只包含一行),我们不断地打印这些增长的文件一遍又一遍。
如果你遇到问题,试着增加睡眠时间。也许sed 's/.*\r//'代替sed ''可以加快速度。tail -c100肯定会有帮助,但是会在输出中插入临时文件名。
正确的处理方法应该是...

  • 使用FIFO代替文件(参见mkfifo命令)。
  • 删除status=progress并定期...
  • 为所有 * 正在运行的dd作业调用kill -s USR1

这使得它们打印单个状态行。

  • 通过阅读fifos* 更新一些内部状态数组。
  • 打印阵列的所有状态。
  • 在某个时候,只有少数dd作业会运行,而其他作业已经完成并关闭了它们的fifos,这使得这个过程有点复杂,这是我坚持使用文件来回答这个问题的主要原因。
4xy9mtcn

4xy9mtcn2#

使用协进程,您可以执行以下操作

#! /bin/bash

coproc DD1 { dd if=/dev/zero of=$drive1 status=progress 2>&1; }
coproc DD2 { dd if=/dev/zero of=$drive2 status=progress 2>&1; }
coproc DD3 { dd if=/dev/zero of=$drive3 status=progress 2>&1; }

el=$(tput el)
cuu1=$(tput cuu1)

IFS=
while :
do
    for n in {1..3}
    do
        v="DD$n"
        if read -r -d $'\r' -u ${!v[0]} line
        then
            printf '%s%s: %s\r\n' "$el" "$v" "$line"
        else
            printf '%s: Done\r\n' "$el" "$v"
        fi
    done
    for n in {1..3}
    do
        printf '%s' "$cuu1"
    done
done

我的dd没有status,所以我假设dd添加了\r,而不是\n

相关问题