Most below there is quick and dirty sh
script designed to work as a filter (between tar
and split
in your case). It was built in Ubuntu and may need some adjustments for other systems (e.g. I'm not sure if column -t | cut -d " " -f 7
is the right way to parse df
regardless of OS). It requires /proc
.
Save it as ensuredf
where your $PATH
points to, make executable (chmod -x ensuredf
) and use like this:
… | ensuredf path requirement | …
where
path
is the directory you want to monitor;
requirement
is the desired free space (df -B
must understand this);
Example:
… | ensuredf /mnt/foo/data/ 2G | …
The idea is to let background cat
pass data from stdin
(of the script) to stdout
but pause it immediately. Then invoke df
for a given path
, parse its output and check if there's more space than requirement
. If so, cat
is resumed, otherwise it is paused. This loops with a hardcoded interval of 1 second as long as /proc
entry for this cat
exists.
Other notes:
- some filesystems (especially BTRFS) make
df
output not as exact as you'd like;
- if your
tar
is very fast and your required space is very low, the interval of 1 second may be too long;
- but even if the interval were zero, when the free space gets below the
requirement
there will be some delay before cat
is paused;
- if for some reason the foreground script gets delayed and the background
cat
works well, the disk may get full anyway.
This means you should set your requirement
with proper safety margin. Use this code as an example and adjust to your needs. I managed to write a safer script that keeps invoking foreground dd
to pass a chunk of data if and only if there's enough disk space, but these multiple dd
processes were a lot slower than a single cat
.
#!/bin/sh
[ $# -eq 2 ] || { printf '%s\n' "usage: $0 path requirement" >&2 ; exit 1;}
pth="$1"
rqrmnt="$2"
intrvl=1
</proc/$$/fd/0 cat >/proc/$$/fd/1 &
kill -s STOP $!
while [ -d /proc/$! ] ; do
if [ $(df -P -B "$rqrmnt" "$pth" | tail -n 1 | column -t | cut -d " " -f 7) -ge 2 ]
then kill -s CONT $!
else kill -s STOP $!
fi
sleep "$intrvl"
done