[Toybox] Would someone please explain what bash is doing here?
Rob Landley
rob at landley.net
Tue Mar 31 21:42:26 PDT 2020
On 3/31/20 9:23 AM, Rob Landley wrote:
> On 3/30/20 5:59 PM, Rob Landley wrote:
>> On 3/30/20 5:45 PM, Rob Landley wrote:
>>> Bash's handling of $SECONDS is kind of inconsistent:
>>>
>>> $ SECONDS=0; echo $SECONDS # why does this print nothing?
>>> $ readonly SECONDS; declare -p SECONDS ; SECONDS=0
>>> declare -ir SECONDS="6"
>>> $ echo $SECONDS # readonly was ignored, assignment happened anyway
>>> 10
>>> $ SECONDS=123+456 # it STARTS integer, but integer assignment sets it to 0
>>> $ echo $SECONDS
>>> 6
>>> $ declare -i SS
>>> $ SS=123+456
>>> $ echo $SS # integer assignment works elsewhere just fine?
>>> 579
>>>
>>> I can't be the first person to test these corner cases, can I? (Version 4.4.12
>>> as in devuan ascii.)
>>>
>>> The downside is I'm trying to get a test suite that both bash and toysh passes,
>>> and to do that I'd have to reproduce it ignoring -i and ignoring -r and I kinda
>>> dowanna?
>>
>> Aha:
>>
>> $ readonly SECONDS; SECONDS=0; echo $SECONDS; echo hello
>> $ readonly SECONDS; SECONDS=0
>> $ echo $?
>> 1
>>
>> No error message. Line processing aborted despite ; not being &&.
>>
>> $ POTATO=abc; readonly POTATO; POTATO=42; echo $POTATO
>> bash: POTATO: readonly variable
>>
>> Well, the line aborting despite ; not being && is at least _consistent_, but...
>>
>> $ POTATO=123 echo hello
>> bash: POTATO: readonly variable
>> hello
>>
>> Ok, "consistent" is the wrong word.
>>
>> $ bash -c $'SECONDS=42; readonly SECONDS; SECONDS=0\necho $SECONDS'
>> 0
>>
>> And the assignment IS still zeroing it, despite the readonly and error.
>>
>> $ bash -c 'SECONDS=42; echo $SECONDS; SECONDS=123+456; echo $?; echo $SECONDS'
>> 42
>> 0
>> 0
>>
>> And the "ignores integer, goes to zero instead" thing is not an error either.
>>
>> Sigh. At least all this nonsense is reproducible. I can add it to the test suite
>> and make mine do what bash does, I just... really don't want to?
>
> Did you know that when RANDOM is exported, the readonly attribute is ignored
> (but prserved!) in a DIFFERENT way?
>
> $ readonly RANDOM
> $ export RANDOM=0
> $ echo $RANDOM
> 24315
> $ export RANDOM=42
> $ echo $RANDOM
> 11151
> $ declare -p RANDOM
> declare -irx RANDOM="23481"
> $ RANDOM=42
> $ echo $RANDOM $RANDOM
> 17766 11151
>
> Now the assignment goes through, and THEN it errors, I.E. the line is still
> aborted, but for no obvious reason since the assignment was successful!
>
> (Also, exporting resolves the variable once after assigning it, so instead of
> giving the reset value it gives the resolved magic value, and the sequence
> continues from there. Eh, I suppose that makes sense?)
>
> Also, calling declare -p RANDOM re-exports the current value?
>
> $ echo $RANDOM $RANDOM
> 17766 11151
> $ declare -p RANDOM
> declare -irx RANDOM="23481"
> $ declare -p RANDOM
> declare -irx RANDOM="32503"
> $ env | grep RANDOM
> RANDOM=32503
> $ env | grep RANDOM
> RANDOM=32503
>
> but merely resolving it doesn't?
>
> $ RANDOM=42
> $ echo $RANDOM $RANDOM $RANDOM $RANDOM $RANDOM $RANDOM $RANDOM
> 17766 11151 23481 32503 7018 25817 28529
> $ echo $RANDOM
> 9160
> $ env | grep RANDOM
> RANDOM=9160
> $ echo $RANDOM
> 16666
> $ env | grep RANDOM
> RANDOM=9160
>
> Ah, no, that's not what's happening:
>
> $ export RANDOM=42
> $ env | grep RANDOM
> RANDOM=17766
> $ declare -p RANDOM
> declare -ix RANDOM="11151"
> $ env | grep RANDOM
> RANDOM=17766
>
> The actual export is deferred until the first non-builtin is called (echo is a
> builtin), but is then persistent. And the $RANDOM value is CACHED somehow?
> (Notice how declare showed the value that THEN wound up in the environment?)
>
> $ export RANDOM=42; echo $RANDOM; env | grep RANDOM
> 11151
> RANDOM=11151
> $ export RANDOM=42; echo $RANDOM $RANDOM; env | grep RANDOM
> 11151 23481
> RANDOM=23481
>
> Um, what?
>
> $ export RANDOM=42; env | grep RANDOMRANDOM=17766
> $ export RANDOM=42; echo $RANDOM $RANDOM $RANDOM
> 11151 23481 32503
> $ export RANDOM=42; echo $RANDOM $RANDOM $RANDOM $RANDOM $RANDOM $RANDOM
> 11151 23481 32503 7018 25817 28529
> $ export -n RANDOM
> $ RANDOM=42; echo $RANDOM $RANDOM $RANDOM $RANDOM $RANDOM $RANDOM
> 17766 11151 23481 32503 7018 25817
>
> So wait, exporting it resolves the value once but doesn't actually put the
> resolved value into the ENVIRONMENT because that's deferred to exec time?
>
> Then why did it... Wait, what?
>
> To be honest, the behavior of BOTH $SECONDS and $RANDOM is insane enough I kinda
> dowanna exactly match it? The first one I recorded millitime() instead of time()
> so in _my_ shell:
>
> $ bash -c 'SECONDS=42; for i in 1 2 3 4; do sleep .25; echo $SECONDS; done'
> 42
> 43
> 43
> 43
> $ bash -c 'SECONDS=42; for i in 1 2 3 4; do sleep .25; echo $SECONDS; done'
> 42
> 42
> 42
> 43
>
> gives consistent output.
>
> And RANDOM... I understand wanting a repeatable sequence of pseudo-random
> numbers, but I dunno what prng they're using so I can't get the SAME sequence
> (ltrace isn't catching anything with "rand" in the name and I refuse to look at
> GPLv3 source code and potentially open myself to insane
> https://lwn.net/Articles/193852/ style lawsuits).
>
> Plus I seem to have already done more testing on combining magic, global,
> readonly, and integer flags than the cumulative history of the project so far.
> Yes it still gets integer wrong:
>
> $ bash -c 'RANDOM=42; echo $RANDOM $RANDOM $RANDOM'
> 17766 11151 23481
> $ bash -c 'RANDOM=42; echo $RANDOM $RANDOM $RANDOM'
> 17766 11151 23481
> $ bash -c 'RANDOM=40+2; echo $RANDOM $RANDOM $RANDOM'
> 16920 13741 518
> $ bash -c 'RANDOM=0; echo $RANDOM $RANDOM'
> 20034 24315
> $ bash -c 'RANDOM=potato; echo $RANDOM $RANDOM'
> 20034 24315
>
> declare -p things it's integer, but assignment does not. In fact...
>
> $ RANDOM=40; echo $RANDOM $RANDOM
> 16920 13741
>
> Yup, smells like an atoi() with no error checking. (No WONDER this thing's a
> megabyte executable, there don't seem to be any common codepaths ANYWHERE...)
>
> Rob
>
> P.S. Why is $GROUPS an array? If you just resolve $GROUPS you get $(id -g) but
> wouldn't it make more sense since it's PLURAL for it to be $(id -G) since it's
> NUMERIC and we have FOR LOOPS? I mean:
>
> $ X="one two three"
> $ for i in $X; do echo $i; done
> one
> two
> three
>
> Variable with spaces in it is a THING. Why gratuitously involve ARRAYS here? I'm
> CONFUSED!
$ export SECONDS
$ env | grep SECONDS
SECONDS=4
$ env | grep SECONDS
SECONDS=4
$ env | grep SECONDS
SECONDS=4
$ export SECONDS
$ env | grep SECONDS
SECONDS=4
$ echo $SECONDS
91
$ env | grep SECONDS
SECONDS=4
I do not understand the rationale behind this at all.
Rob
More information about the Toybox
mailing list