I am writing a shell script which works on my local /bin/sh
fine (dash on Ubuntu 13.04), but I unltimately need to run it on a dumb box where I'm getting an error because of an operation on variables:
$((n2 - n1 + 1))
doesn't work, I get an error like:
syntax error: you disabled math support for $((arith)) syntax
I don't know a lot about the sh
on there but I think this thing is busybox. How can I do maths on this dumb shell?
edit with list of applets
~ # busybox --list
[
arp
ash
cat
chgrp
chmod
chown
chroot
chvt
clear
cmp
cp
cut
date
dd
deallocvt
df
dmesg
du
echo
env
false
find
freeramdisk
ftpget
ftpput
grep
gunzip
gzip
hexdump
hwclock
ifconfig
ln
losetup
ls
md5sum
mkdir
mkfifo
mknod
mkswap
more
mount
mv
nslookup
ping
ping6
ps
pwd
renice
reset
rm
rmdir
route
seq
sh
sha1sum
sha256sum
sleep
sort
swapoff
swapon
switch_root
sync
tar
taskset
tee
telnet
test
tftp
time
top
touch
true
umount
uname
uniq
uptime
usleep
vconfig
vi
wget
whoami
yes
seq
+grep
+sort
Notes:
subtract_nonposix
which relies on a grep
supporting -w
and -B
(non-POSIX, but even busybox
' grep
supports them)add
/subtract
support only unsigned integers as inputmultiply
/divide
support signed integers as inputsubtract
/multiply
/divide
can deal with negative resultsmultiply
/divide
might be very costly (see comments)subtract
/multiply
may pollute your namespace (they use $__x
and $__y
respectively) if not used in a subshellarith.sh
:
#!/bin/sh
is_uint()
{
case "$1" in
''|*[!0-9]*) return 1
;;
esac
[ "$1" -ge 0 ]
}
is_int()
{
case "${1#-}" in
''|*[!0-9]*) return 1
;;
esac
}
# requires seq, grep -n, sort -nr
# reasonably fast
add()
{
if ! is_uint "$1" \
|| ! is_uint "$2"; then
echo "Usage: add <uint1> <uint2>"
return 1
fi
[ "$1" -eq 0 ] && { echo "$2"; return; }
[ "$2" -eq 0 ] && { echo "$1"; return; }
{
seq 1 "$1"
seq 1 "$2"
} \
| grep -n "" \
| sort -nr \
| { read num; echo "${num%[-:]*}"; }
}
# requires seq, grep -n, sort -nr, uniq -u
# reasonably fast
subtract()
{
if ! is_uint "$1" \
|| ! is_uint "$2"; then
echo "Usage: subtract <uint1> <uint2>"
return 1
fi
if [ "$1" -ge "$2" ]; then
__x="$1"
__y="$2"
else
__x="$2"
__y="$1"
fi
{
seq 0 "${__x}"
seq 0 "${__y}"
} \
| sort -n \
| uniq -u \
| grep -n "" \
| sort -nr \
| \
{
read num
: ${num:=0}
[ "${__x}" = "$2" ] && [ "$1" -ne "$2" ] && minus='-'
echo "${minus}${num%:*}"
}
}
# requires seq, grep -wB
# faster than subtract(), but requires non-standard grep -wB
subtract_nonposix()
{
if ! is_uint "$1" \
|| ! is_uint "$2"; then
echo "Usage: subtract <uint1> <uint2>"
return 1
fi
if [ "$1" -ge "$2" ]; then
__x="$1"
__y="$2"
else
__x="$2"
__y="$1"
fi
seq 0 "${__x}" \
| grep -w -B "${__y}" "${__x}" \
| \
{
read num
[ "${__x}" = "$2" ] && [ "$1" -ne "$2" ] && minus='-'
echo "${minus}${num}"
}
}
# requires seq, sort -nr, add()
# very slow if multiplicand or multiplier is large
multiply()
{
if ! is_int "$1" \
|| ! is_int "$2"; then
echo "Usage: multiply <int1> <int2>"
return 1
fi
[ "$2" -eq 0 ] && { echo 0; return; }
# make sure to use the smaller number for the outer loop
# to speed up things a little if possible
if [ $1 -ge $2 ]; then
__x="$1"
__y="$2"
else
__x="$2"
__y="$1"
fi
__x="${__x#-}"
__y="${__y#-}"
seq 1 "${__y}" \
| while read num; do
sum="$(add "${sum:-0}" "${__x}")"
echo "${sum}"
done \
| sort -nr \
| \
{
read num
if [ "$1" -lt 0 -a "$2" -gt 0 ] \
|| [ "$2" -lt 0 -a "$1" -gt 0 ]; then
minus='-'
fi
echo "${minus}${num}"
}
}
# requires subtract()
# very costly if dividend is large and divisor is small
divide()
{
if ! is_int "$1" \
|| ! is_int "$2"; then
echo "Usage: divide <int1> <int2>"
return 1
fi
[ "$2" -eq 0 ] && { echo "division by zero"; return 1; }
(
sum="${1#-}"
y="${2#-}"
count=
while [ "${sum}" -ge "${y}" ]; do
sum="$(subtract "${sum}" "${y}")"
# no need to use add() for a simple +1 counter,
# this is way faster
count="${count}."
done
if [ "$1" -lt 0 -a "$2" -gt 0 ] \
|| [ "$2" -lt 0 -a "$1" -gt 0 ]; then
minus='-'
fi
echo "${minus}${#count}"
)
}
echo "10 4 14
4 10
10 10
2 -2
-2 -2
0 0
x y" | while read x y; do
for op in add subtract subtract_nonposix multiply divide; do
printf -- "${x} ${y} %-17s = %s\n" "${op}" "$("${op}" "${x}" "${y}")"
done
echo
done
Example run:
$ ./arith.sh
10 4 add = 14
10 4 subtract = 6
10 4 subtract_nonposix = 6
10 4 multiply = 40
10 4 divide = 2
4 10 add = 14
4 10 subtract = -6
4 10 subtract_nonposix = -6
4 10 multiply = 40
4 10 divide = 0
10 10 add = 20
10 10 subtract = 0
10 10 subtract_nonposix = 0
10 10 multiply = 100
10 10 divide = 1
2 -2 add = Usage: add <uint1> <uint2>
2 -2 subtract = Usage: subtract <uint1> <uint2>
2 -2 subtract_nonposix = Usage: subtract <uint1> <uint2>
2 -2 multiply = -4
2 -2 divide = -1
-2 -2 add = Usage: add <uint1> <uint2>
-2 -2 subtract = Usage: subtract <uint1> <uint2>
-2 -2 subtract_nonposix = Usage: subtract <uint1> <uint2>
-2 -2 multiply = 4
-2 -2 divide = 1
0 0 add = 0
0 0 subtract = 0
0 0 subtract_nonposix = 0
0 0 multiply = 0
0 0 divide = division by zero
x y add = Usage: add <uint1> <uint2>
x y subtract = Usage: subtract <uint1> <uint2>
x y subtract_nonposix = Usage: subtract <uint1> <uint2>
x y multiply = Usage: multiply <int1> <int2>
x y divide = Usage: divide <int1> <int2>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With