Reputation: 548
I have a script which decompresses .gz
file and copies it into block device. I use dd
to get an information about the progress (I cannot use other utilities, like pv
to do that). Here is a part of the script:
#!/bin/sh
set -e
trap 'true' USR1
# ...
gunzip -c "${SVM_IMAGE_FILE_PATH}" | dd iflag=fullblock of="${TARGET_DEVICE_PATH}" bs=64M 2>"${DD_OUTPUT_FILE_PATH}" &
DD_PID=$!
echo "Decompression process has started: $$ -> ${DD_PID}" >> "${DECOMPRESSION_LOG_FILE_PATH}"
# Wait for dd process to become ready to receive 'kill -s USR1' signals
sleep 1
while (ps -p "${DD_PID}") >/dev/null 2>&1
do
set +e
kill -s USR1 $DD_PID 2>/dev/null
LAST_LINE=`tail -n 1 "${DD_OUTPUT_FILE_PATH}"`
case "${LAST_LINE}" in
*byte*)
BYTES_COPIED=`echo ${LAST_LINE} | cut -d ' ' -f1`
PROGRESS=$(( $(( $BYTES_COPIED * 100 )) / $DECOMPRESSED_FILE_SIZE ))
set_status "${PROGRESS}% processed"
;;
esac
# ...
set -e
sleep 1
done
As you can see there is a strage sleep 1
before the while
loop. I want to get rid of it, but I don't know how. If I delete this sleep 1
, the dd
process will be killed right after the first invocation of kill -s USR1 $DD_PID
. Maybe that doesn't kill dd
, but it somehow affects gunzip
process — I don't know what is really happening.
So, is there a way to get rid of that sleep 1
?
Upvotes: 1
Views: 3103
Reputation: 123410
There are two issues:
DD_PID
is the pid of the shell that runs your pipeline, not of dd
.SIGUSR1
will kill a process if it's not ready to receive the signal.The shell may choose to execute dd
in the same process as an optimization, but this is not something you can depend on (especially not when using /bin/sh
.
You can also not depend on dd
racing to set up a signal trap, so it's better to set it up yourself.
Since you're on Linux, consider just using the GNU dd flag status=progress
for an automatic status bar.
Alternatively, change the shebang to #!/bin/bash
and use:
trap '' USR1 # Ignore USR1 in inherited processes by default
dd iflag=fullblock of="${TARGET_DEVICE_PATH}" bs=64M 2>"${DD_OUTPUT_FILE_PATH}" \
< <(gunzip -c "${SVM_IMAGE_FILE_PATH}") &
dd_pid=$! # Is now 'dd's pid and not a shell's.
Or to be sh
compliant:
fifo="/tmp/foo"
mkfifo "$fifo"
gunzip -c "${SVM_IMAGE_FILE_PATH}" | {
trap '' USR1
dd iflag=fullblock of="${TARGET_DEVICE_PATH}" bs=64M 2>"${DD_OUTPUT_FILE_PATH}" &
echo "$!" > "$fifo"
wait
} &
read dd_pid < "$fifo"
rm "$fifo"
Or with dash
, change the shebang to #!/bin/dash
and change trap 'true' USR1
to trap '' USR1
.
Upvotes: 1