7

I am trying to calculate the used bandwidth on the Ethernet interface (which is 1000 Mbit/s). To test my script, I am using the iperf tool to generate huge bandwidths.

The problem I am facing is when eth0_rx1 and eth0_rx2 gets the values which are greater than maximum 32-bit value. I am getting the difference as 0.

Somehow

printf 'eth0 Download rate: %s B/s\n' "$((eth0_rx2-eth0_rx1))"

is giving the correct value, but when tried with

eth0_diff=expr $eth0_rx2 - $eth0_rx1

I am getting the value 0.

Is there a way to handle if rx_bytes or tx_bytes are more than 32 bits?

I am not sure this is an elegant way of calculating used bandwidth. If not, please suggest other alternate way.

Sample output:

eth0_rx1 = 2134947002 \
eth0_rx2= 2159752166 \
eth0 Download rate: 24805164 B/s \
eth0_diff = 12536645 \
eth0_rx_kB = 12242 \
eth0_rx_kB_100 = 1224200 \
eth0_rx_kB_BW = 9

eth0_rx1 = 2159752166 \
eth0_rx2= 2184557522 \
eth0 Download rate: 24805356 B/s \
eth0_diff = 0 \
eth0_rx_kB = 0 \
eth0_rx_kB_100 = 0 \
eth0_rx_kB_BW = 0

Script used:

#!/bin/sh

eth0_rx1=$(cat /sys/class/net/eth0/statistics/rx_bytes)
while sleep 1; do
    eth0_rx2=$(cat /sys/class/net/eth0/statistics/rx_bytes)
    echo "eth0_rx1 = $eth0_rx1"
    echo "eth0_rx2= $eth0_rx2"
    printf 'eth0 Download rate: %s B/s\n' "$((eth0_rx2-eth0_rx1))"

    eth0_diff=`expr $eth0_rx2 - $eth0_rx1`
    echo "eth0_diff = $eth0_diff"

    #convert bytes to Kilo Bytes
    eth0_rx_kB=`expr $eth0_diff / 1024`
    echo "eth0_rx_kB = $eth0_rx_kB"

    #bandwidth calculation
    eth0_rx_kB=`expr $eth0_rx_kB \* 100`
    echo "eth0_rx_kB_100 = $eth0_rx_kB"
    #125000 = 1000 Mbit/s
    eth0_rx_kB=`expr $eth0_rx_kB / 125000`
    echo "eth0_rx_kB_BW = $eth0_rx_kB"

    eth0_rx1=$eth0_rx2
    eth2_rx1=$eth2_rx2
done
6
  • are you on a 32-bit platform? Which one? – ilkkachu Jun 23 at 13:23
  • 1) there is some confusion, expr is not an internal shell part, unlike $(( ...)) 2) taking your figures my expr (ubuntu 20.10) give me correct values 3) does bc give correct values (e.g. x=$(echo $eth0_rx2 - $eth0_rx1 | bc) ) ? – Archemar Jun 23 at 13:57
  • @ilkkachu, I am running on custom Linux box of i686 GNU/Linux – Cheppy Jun 23 at 14:20
  • @Archemar,I dont have bc utility on my Box, as you say is there alternate for expr, so that o get correct vales – Cheppy Jun 23 at 14:23
  • 1
    Why is this question tagged for Bash, when /bin/sh is likely not Bash? Even if it is Bash, it can behave very differently in POSIX-conformant mode. – Toby Speight Jun 24 at 7:34
17

Given that printf 'eth0 Download rate: %s B/s\n' "$((eth0_rx2-eth0_rx1))" is giving you the correct value, as long as integer arithmetic is good enough, you’ve got your answer: $((eth0_rx2-eth0_rx1)), i.e. shell arithmetic.

Many shells, notably Bash, use 64-bit integers, even on 32-bit platforms.

Thus:

    eth0_diff=$((eth0_rx2 - eth0_rx1))
...
    eth0_rx_kB=$((eth0_diff / 1024))
...
    eth0_rx_kB=$((eth0_rx_kB * 100))
...
    eth0_rx_kB=$((eth0_rx_kB / 125000))

GNU expr can support arbitrary-precision arithmetic, if it is built with the GNU MP library. In other cases it uses native integers, and apparently on your system (assuming you’re using GNU expr) those are 32 bits in size. Other implementations probably have similar limits.

8
  • Other shells can also support 64-bit arithmetic, not just Bash. GNU coreutils expr seems to use intmax_t, so it could easily support 64-bit too. Even if e.g. on x86-64, an int is still 32 bits. GNU expr also croaks on overflow, e.g. expr 9223372036854775807 + 1 gives expr: +: Numerical result out of range. The one from Busybox overflows as usual. I wonder what platform and system they're running on. – ilkkachu Jun 23 at 13:29
  • 1
    arithmetic expansion is in POSIX, right? Apart from some operators like **and += etc., that is. The basic stuff used here should be common. Yes, they should use /bin/bash if they want Bash, I was just wondering if it's needed here. Though Bash does use intmax_t for arithmetic, so at least that helps with not having to configure the arithmetic explicitly... – ilkkachu Jun 23 at 14:27
  • 1
    @ilkkachu oh silly me, yes, it is! – Stephen Kitt Jun 23 at 14:32
  • 1
    @ilkkachu, += is in POSIX, it's ++, -- that's not. – Stéphane Chazelas Jun 24 at 16:25
  • 1
    @TankOrSmash not quite; it says that bash uses intmax_t. On 32-bit Linux with glibc, intmax_t is 64 bits in length. – Stephen Kitt Jun 24 at 18:17
7

bash does use 64bit integers:

$echo $((2**63-1))
9223372036854775807
$echo $((2**63))
-9223372036854775808
2
  • if so , how come my statement eth0_diff=expr $eth0_rx2 - $eth0_rx1 is giving zero for the second case – Cheppy Jun 23 at 12:48
  • 11
    @Cheppy: You are using expr and not shell arithmetic but also you are using /bin/sh and not bash. – jesse_b Jun 23 at 12:52
3

The standard arbitrary-precision calculator is dc. We can use this for large integer arithmetic:

eth0_diff=$(dc -e "$eth0_rx2 $eth0_rx1 -p")

You can even get dc to do the printing, using the n and p commands:

#!/bin/sh

set -eu

netstatfile=/sys/class/net/eth0/statistics/rx_bytes
test -r "$netstatfile"

eth0_rx1=$(cat "$netstatfile")

while sleep 1
do
    eth0_rx2=$(cat "$netstatfile")
    dc -e "$eth0_rx1[eth0_rx1 = ]np $eth0_rx2[eth0_rx2 = ]np" \
       -e 'r-[eth0_diff = ]np [Download rate: ]np 1024/[eth0_rx_kB = ]np' \
       -e '100/[eth0_rx_kB_100 = ]np 125000/[eth0_rx_kB_BW = ]np'
    eth0_rx1=$eth0_rx2
done

You may find the numfmt tool (part of GNU coreutils) useful for dividing by decimal or binary thousands. It's capable of dealing with very large numbers (though admittedly not the strange mixture used here).

1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.