help-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Can Bash do simple math?


From: Greg Wooledge
Subject: Re: Can Bash do simple math?
Date: Mon, 5 Aug 2024 19:10:05 -0400

On Mon, Aug 05, 2024 at 23:42:44 +0200, bash@blaklinten.xyz wrote:
> The minimal example code to produce an error:
> https://gist.github.com/blaklinten/3dd29f3f73b75337a86b8d7cdb59bdd7

Please post the code directly in the mailing list, so that it becomes
part of the archive.  Code that's on an external web page may vanish
at some point in the future.  Also, making people *work* extra hard to
see the example code is not a good way to entice people to help.

>   ((DIFF = $2 - $1))
>   ((SECONDS = DIFF % 60))
>   ((MINUTES = (DIFF % 3600) / 60))
>   ((HOURS = DIFF / 3600))
> 
>   echo "$HOURS:$MINUTES:$SECONDS"

My first comment is that by using all-caps variable names, you're stepping
on internal variables that bash uses for its own purposes.  SECONDS has
a special meaning.  By assigning to it, you cause it to lose that special
meaning, and become just a regular variable... maybe.  I doubt many people
have actually tested this, especially when the assignment is happening
as a side effect within an arithmetic context, rather than a true shell
assignment statement.  There could be a weird bug here.

Avoid all-caps variable names for precisely this reason.

I wonder if the bug you're seeing goes away if you rename your variables.

My second comment: in your posted code, you write an infinite loop that
calls the diff() function inside a command substitution.  You appear
to be saying that the function does not give the same result every time
it's called -- but you aren't showing the values of the variables when
it calculates the wrong result, making it harder to debug what's gone
wrong.

It might be helpful if your function would write all the variables'
values to stderr, which you can have redirected to a file.  Then, after
you detect an error, you can look for non-identical lines in the file.

Given that you're forking a subshell process inside an infinite loop,
you may also be running into weirdness when bash fills up its internal
table of exit statuses.

Do your errors always appear after many thousands of iterations?  Or do
they appear earlier sometimes?

I wonder if your bug goes away if you stop calling the function in a
command substitution.  Something like this, perhaps:


#!/bin/bash
exec 2>bash-math.log
trap break INT

diff() {
    ((diff = $2 - $1))
    ((seconds = diff % 60))
    ((minutes = (diff % 3600) / 60))
    ((hours = diff / 3600))
    result="$hours:$minutes:$seconds"
    printf >&2 'diff=%s seconds=%s minutes=%s hours=%s result=%s\n' \
        "$diff" "$seconds" "$minutes" "$hours" "$result"
}

start=1722888722
end=$((start + 3695))
expected='1:1:35'
errors=0
rounds=0

while ((errors < 3 && rounds < 10000000)); do
    if ((++rounds % 100 == 0)); then
        printf '\r\e[0K%s processed.' "$ROUNDS"
    fi
    diff "$start" "$end"
    if [[ "$result" != "$expected" ]]; then
        ((++errors))
        printf '\nRound %s unexpected result: %s\n' "$rounds" "$result"
    fi
done
printf '\n%s rounds, %s errors.\n' "$rounds" "$errors"


If this doesn't generate errors, but your original does, then you'll have
to figure out which one of my changes made the errors go away.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]