These function are called as
add variable numbers subtract variable numbers multiply variable numbers divide variable numberswhere
variable is the name of a shell variable where the
result will be stored.
To generate the "operand" files, we use the function
zero.
We need to make a special case for 0, because dd won't
allow us to copy "the first 0 bytes" of a file. But, of
course, it is easy to add 0 to a number!
At the end of the processing, we use dd again to copy
the result file to /dev/null - as a side effect, we
get on the standard error file the number of bytes copied, which is
the required result. Well, we actually get the number of records
copied, but we set the record size to 1. The result looks like:
125+0 records in 125+0 records outso the text up to the first "+" is the required length. We split the result assigning "+" to the shell's fields separator variable.
Finally, the code:
add () {
result="$1"
shift
# start with an empty file (copy /dev/null)
$dd if=/dev/null of=$tmp bs=1 2>/dev/null
# for each number given, add the required size to the current file
for n in "$@"
do
case "$n" in
0) ;;
*) # generate a file of length $n
zero | $dd of=$tmp.1 bs=1 "count=$n" 2>/dev/null
# append it to the current file
( $dd if=$tmp; $dd if=$tmp.1 ) 2>/dev/null | $dd of=$tmp.2 2>/dev/null
$dd if=$tmp.2 of=$tmp 2>/dev/null
;;
esac
done
# figure out the size of the result
IFS="+"
set `$dd if=$tmp bs=1 of=/dev/null 2>&1`
IFS="$saveIFS"
# assign to the desired variable
eval $result='$1'
}
subtract () {
result="$1"
# generate a file of size n1
zero | $dd of=$tmp bs=1 "count=$2" 2>/dev/null
# compute the length of the result when we skip n2 bytes
IFS="+"
set `$dd if=$tmp bs=1 of=/dev/null "skip=$3" 2>&1`
IFS="$saveIFS"
# check if "dd" produced an error
case "$1" in
dd*) set 0 ;;
esac
# and assign to the required variable
eval $result='$1'
}
dd to count "n" records of
length 1. If we set the length to something else, the resulting
file's length will be the product of the two.
multiply () {
result="$1"
# generate a file with n1 records of size n2
zero | $dd "bs=$2" of=$tmp "count=$3" 2>/dev/null
# the resulting file has size n1 * n2 bytes - get its size
IFS="+"
set `$dd if=$tmp bs=1 of=/dev/null 2>&1`
IFS="$saveIFS"
# and assign the result to the required variable
eval $result='$1'
}
dd will report the partial record
as "33+1 records in", but unfortunately it always says
"+1" (it is the number of partial records, not the
number of bytes in the record), so we need to use divide, multiply,
and subtract to get the reminder.
divide () {
result="$1"
# generate a file of size n1
zero | $dd bs=1 of=$tmp "count=$2" 2>/dev/null
# and read it with record size n2 - the number of complete
# records is int(n1/n2)
IFS="+"
set `$dd if=$tmp "bs=$3" of=/dev/null 2>&1`
IFS="$saveIFS"
# assign the result
eval $result='$1'
}