2

The content of a.sh is

echo start
let index=(RANDOM % 4)
echo $index

a.sh sometimes fails to produce the number

gqqnbig@instance-1:~/test/systemd$ vim a.sh
gqqnbig@instance-1:~/test/systemd$ bash -e a.sh
start
3
gqqnbig@instance-1:~/test/systemd$ bash -e a.sh
start
3
gqqnbig@instance-1:~/test/systemd$ bash -e a.sh
start
2
gqqnbig@instance-1:~/test/systemd$ bash -e a.sh
start
2
gqqnbig@instance-1:~/test/systemd$ bash -e a.sh
start
2
gqqnbig@instance-1:~/test/systemd$ bash -e a.sh
start
3
gqqnbig@instance-1:~/test/systemd$ bash -e a.sh
start
3
gqqnbig@instance-1:~/test/systemd$ bash -e a.sh
start
gqqnbig@instance-1:~/test/systemd$ echo $?
1
gqqnbig@instance-1:~/test/systemd$

See the last time I run a.sh, it only outputs "start" but not the index, which means let index=(RANDOM % 4) has something wrong, and the exit code is 1.

Why does the RANDOM variable produce this random error? How do I fix it?

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
Gqqnbig
  • 5,845
  • 10
  • 45
  • 86
  • What happens if you use `let index=($RANDOM % 4)`? – Oppen Apr 30 '20 at 03:16
  • Nevermind, I observe the same behavior with both combinations. A simple test to trigger: `for i in {1..10}; do { let test=($RANDOM % 4) && echo $test; } || break; done`. It will try 10 times (enough to fail for sure in my case), breaks at first failure. – Oppen Apr 30 '20 at 03:20
  • It doesn't fail if using traditional arithmetic expansion instead of `let`. – Oppen Apr 30 '20 at 03:21
  • Fun thing: `let test=(0 % 4)` always fails. Maybe that's where we should start looking? I tend to think there's something wrong with the usage rather than bugs, but I'm not sure how to dig the documentation, and it seems counter intuitive. – Oppen Apr 30 '20 at 03:25
  • 1
    @Oppen have you read the posted answer yet? – rtx13 Apr 30 '20 at 03:28
  • Even funnier: whenever the result of the modulus is `0`, it reports failure. It does assign the result, though. – Oppen Apr 30 '20 at 03:31
  • Oops, failed to refresh. Thanks. – Oppen Apr 30 '20 at 03:32
  • 1
    See the exercises in [BashFAQ #105](https://mywiki.wooledge.org/BashFAQ/105#Exercises), regarding the myriad of ways `bash -e` makes your code less predictable, less portable, more contextually-dependent, and harder to reliably review. – Charles Duffy Apr 30 '20 at 03:43

1 Answers1

5

Using -e causes bash to terminate on error.

When let evaluates to 0, it is considered an error. Bash will terminate the script before printing the value 0.


To avoid undesired termination you can use one of the following approaches:

  1. Don't use the -e flag

  2. Ensure the statement always succeeds:

let 'index=(RANDOM % 4)' || true

or

let 'index=(RANDOM % 4)' || :
  1. Avoid using let (per @LéaGris):
index=$((RANDOM % 4))
rtx13
  • 2,580
  • 1
  • 6
  • 22
  • To protect the arithmetic expression from returning failure, use this form instead: `bash -ec 'for i in {1..10}; do { test=$((RANDOM % 4)); echo $test; } || break; done'` – Léa Gris Apr 30 '20 at 03:33
  • 2
    I failed to state that I understand the implication of `-e` flag, but I have no authority to remove this flag. It's the default flag in GitHub Actions, they do this for a reason. But thanks so much for the explanation of `let`. – Gqqnbig Apr 30 '20 at 03:58