I want to run multiple dd commands in background, but be able to see the status.
I have the following 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."
Which results in the following output:
$ 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
Is there any way how to output the respective dd statuses below the drive paths? For example:
$ 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
I tried various redirects, named pipes etc. but wasn't able to achieve such (or similar) output.
I tried the coprocesses approach which seems to be the way to go, but now I'm unable to make it work with the for cycle.
This works fine:
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
However this:
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
doesn't work for the second coprocess:
sda PID: 12759
./wipe.sh: line 39: warning: execute_coproc: coproc [12759:dd_sda] still exists
sdb PID:
When hardcoding the name using if condition, it also works:
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
Here is my final solution, massively inspired by @Diego Torres Milano (thanks again).
#!/usr/bin/env bash
drives=$@
number_of_drives=$#
drives_to_wipe=($drives)
stripped_drive_path() {
echo $1 | awk -F/ '{ print $3 }'
}
wipe() {
echo "Filling \`$1\` drive with zeros:"
dd if=/dev/zero of=$1 status=progress 2>&1
}
echo "Total number of drives: $number_of_drives"
# Wipe the drives in parallel
iteration=1
for drive in $drives; do
drive_name=$(stripped_drive_path $drive)
coproc_name=dd_${drive_name}
eval coproc $coproc_name "{ wipe $drive; }"
if [[ "$iteration" == 1 ]]; then
echo 'Feel free to ignore the following warnings'
fi
((iteration++))
done
# Display the progress
iteration=1
# Run until all drives are wiped
while [[ ${#drives_to_wipe[@]} > 0 ]]; do
for drive in $drives; do
drive_name=$(stripped_drive_path $drive)
coproc_name=dd_${drive_name}
# Move one line below the "Filling drive with zeros" message
if [[ $iteration > 1 ]]; then
tput cud 1
fi
# Read the drive's current status from the coprocess
if read -r -d $'\r' -u ${!coproc_name[0]} line &> /dev/null; then
tput el # Clear line
echo -e "$line\r\n"
else
# Remove the finished drive from the list
for i in ${!drives_to_wipe[@]}; do
if [ "${drives_to_wipe[$i]}" == "$drive" ]; then
unset drives_to_wipe[$i]
fi
done
tput el # Clear line
echo -e "$(($number_of_drives - ${#drives_to_wipe[@]}))/$number_of_drives done!\r\n"
fi
# Move back one line up
if [[ $iteration > 1 ]]; then
tput cuu 1
fi
done
# Move two lines up for each drive
if [[ ${#drives_to_wipe[@]} > 0 ]]; then
tput cuu $(($number_of_drives * 2))
fi
((iteration++))
done
echo "All drives ($number_of_drives) wiped."
Which results in such output:
$ sudo bash ./script.sh /dev/sda /dev/sdb
Total number of drives: 2
Feel free to ignore the following warnings
./wipe.sh: line 24: warning: execute_coproc: coproc [85994:dd_sda] still exists
Filling `/dev/sda` drive with zeros:
1/2 done!
Filling `/dev/sdb` drive with zeros:
2/2 done!
All drives (2) wiped.