A Little Bit Louder NowTim Hammerquist March 12, 2021 #linux #macos #shell #unix
There aren't many Unix utilities more Unixy than
dd(1). It takes data from the
input file and writes it to the output file — but it's kind of shy. With
very little work, we can convince
dd to give us status updates!
DD R U OK?
For almost half a century,
dd has been the go-to Unix utility for copying raw
data from place to place, and it's still one of the most common tools used to
write disk images (e.g., Linux distribution images) onto discs and USB sticks.
But for all the time, it's been pretty reticent. You invoke it from the command line. Give it the files/devices to operate on, maybe a parameter or two, and off it goes... you hope. I still recall writing 4+GB Linux DVD-ROM ISOs to USB thumb drives, watching that unchanging cursor, and wondering how many days would pass until it finished.
loudd is a drop-in replacement for
dd. It doesn't turn
dd into a font of
elegance, but it does make writing images a little more verbose.
Poking the PID
It turns out that
dd(1) will happily provide an update on its progress. It
just needs a little prodding.
SIGINFO... signal, the current input and output blockcounts will be written to the standard error output in the same format as the standard completion message.
Here's a wrapper script I've called
loudd that takes advantage of this. We'll
dig into it a bit more below.
#!/bin/sh # loudd - a louder dd & pid= while ; do done
From a high level, here's what we're doing:
ddwith the arguments passed to
- Send it to the background (
&), capturing its pid (
- For as long as
- ... send it a
- ... sleep for 1 second
- ... send it a
And here's what it looks like in action:
) ) ) )
In this example,
loudd sends 3
SIGINFO signals to
dd before it completes,
triggering the first 3 stat stanzas.
dd sends its regularly scheduled block of statistics.
Keep reading for more detail on how it works.
Break It Down
This line invokes the actual
dd process. The
$@ shell variable expands to
anything passed after the script's name.
loudd if=a_file of=another_file, the
$@ variable will contain the
of=another_file. The resulting command
dd if=a_file of=another_file &.
Note that there's a another shell variable
$* which is very similar, but it
doesn't treat the arguments as separate parts. For example, by contrast:
loudd if=a_file of=another_file, the result would be
dd "if=a_file of=another_file" &, which won't make any sense to
dd into a background process.
Having just started
dd as a background process in the previous line, we use
$! to get its pid and store it in the
$pid variable for
Here we send the
INFO signal to
dd using the pid we just
dd process is still running:
ddwill dump its current stats to its
stderrstream, generally the shell
killwill return 0 (success), since the signal was successfully sent
dd has finished, however:
killwill fail to send the
INFOsignal and return nonzero
dd has terminated,
kill will be unable to send the signal and write an
stderr (fd 2). However, we full expect
dd to terminate eventually,
so the error message can be safely sent to
/dev/null — i.e., silenced.
Sleep for 1 second. This can be any value. I like seeing the status every second or so, if only to reassure me the write hasn't got stuck somewhere. Adjust to taste.
while ; do ; done
while loop. As long as the COND clause returns a successful
status (0), the shell will execute the LOOP clause.
In this case, as long as
dd is still running, we'll send the
INFO signal and
sleep for approximately 1 second.
This isn't a flashy script. Much like
dd itself, it does the bare minimum
asked of it. This has been more than enough for my needs, but it certainly COULD
Some ideas I've spit-balled:
- Parse and convert the bytes read/written to a
curl-style progress meter.
- Compute total bytes to be written, and use bytes written so far to produce a progress bar.
I appreciate being able to use this same script on Linux, macOS, and BSD without any dependencies or changes, but there's nothing stopping you, dear reader, from expanding on this idea.
If you do, or if you learned something from this, drop me a line and tell me about it!
- A feature of some terminals will cause
CTRL-tto send a
SIGINFOsignal to the process attached to the tty. This means that it may be possible to just use
^Tto trigger an ad hoc statistics block.
statusoption that provides similar functionality to
loudd. However, it is limited to ~1 second intervals, and is not available on non-GNU platforms like macOS or BSD.