shopt -u compat* (re)sets BASH_COMPAT to 51
Configuration Information [Automatically generated, do not change]: Machine: x86_64 OS: linux-gnu Compiler: x86_64-pc-linux-gnu-gcc Compilation CFLAGS: -march=native -mtune=native -Wall -pipe -g3 -ggdb3 -gdwarf-4 -O2 -fno-omit-frame-pointer uname output: Linux apgunner 5.15.11 #1 SMP PREEMPT Thu Dec 23 09:06:30 CET 2021 x86_64 Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz GenuineIntel GNU/Linux Machine Type: x86_64-pc-linux-gnu Bash Version: 5.1 Patch Level: 16 Release Status: release Description: The only way to set a compat level of 50 is to use the BASH_COMPAT variable. However, unsetting any compatNN shopt value will (re)set BASH_COMPAT to 51 (with bash-5.1), which is not something users might expect. Technically, the result isn't wrong, since, i.e., shopt -u compat43 did what it was asked to - not setting the compat value to 43. It's counter-intuitive and the old value should be retained, though. Repeat-By: BASH_COMPAT='5.0' declare -p 'BASH_COMPAT' eval "$(shopt -p)" # alternatively, a simple shopt -u compat44 would suffice declare -p 'BASH_COMPAT' OpenPGP_signature Description: OpenPGP digital signature
Interesting bug
Dear all, I think I found a rather interesting bug: ``` #!/bin/bash function badCode { echo "bad code executed" } function testCode { #pick some existing file, nonexisting works too though echo "/etc/passwd" } function tfunc { local foo= foo="$(testCode)" || {echo "foo";} cat "$foo" || { badCode case $? in *) exit 1 esac } } echo "Finished." ``` (I also attached it.) I guess 99% of programmers would either expect "Finished" to be printed or some syntax error. In fact however the `badCode` function is executed. "Finished" is never executed. This is a nice one to hide bad code... Output: ``` cat: '': No such file or directory bad code executed ``` Affected bash versions: Debian 11: GNU bash, version 5.1.4(1)-release (x86_64-pc-linux-gnu) Fedora 32: GNU bash, version 5.0.17(1)-release (x86_64-redhat-linux-gnu) (Probably more, these were the only two I tested.) Happy bug hunting! Best Regards David#!/bin/bash function badCode { echo "bad code executed" } function testCode { #pick some existing file echo "/etc/passwd" } function tfunc { local foo= foo="$(testCode)" || {echo "foo";} cat "$foo" || { badCode case $? in *) exit 1 esac } } echo "Finished." smime.p7s Description: S/MIME Cryptographic Signature
Re: Interesting bug
Hello David! > I guess 99% of programmers would either expect "Finished" to be printed or > some syntax error. Well, 99% of shell programmers will (hopefully ;-) ) put a blank between "{" and "echo" in the line foo="$(testCode)" || {echo "foo";} Best regards Martin
Re: Interesting bug
On 12/02/2022 at 12:23, David Hobach wrote: function tfunc { local foo= foo="$(testCode)" || {echo "foo";} ^ Missing space after opening brace Because the code block is missing a space after the opening brace; the opening brace is ignored, but the following closing brace is parsed as the end for the tfunc function code block. The following statements are executed for the global scope since the function definition block is closed by the faulty syntax above. cat "$foo" || { badCode case $? in *) exit 1 esac } } The bug is in your code. https://shellcheck.net/ static analysis for shell scripts can help you spot such syntax errors. -- Léa Gris
Re: Interesting bug
I guess 99% of programmers would either expect "Finished" to be printed or some syntax error. Well, 99% of shell programmers will (hopefully ;-) ) put a blank between "{" and "echo" in the line foo="$(testCode)" || {echo "foo";} Yes, the interesting part is that depending on which space you accidentally forget, you'll either get the expected "Finished" or "bad code executed". foo="$(testCode)" || {echo "foo"; } # --> bad code foo="$(testCode)" || { echo "foo";} # --> Finished I guess it closes the function and {echo is interpreted as string or so, but that is probably not all (testCode is e.g. never executed). A syntax error would be nice instead. Shellcheck at least gets this unless you do something weird and more obvious such as ``` #!/bin/bash function badCode { echo "bad code executed" } function testCode { #pick some existing file echo "/etc/passwd" } function tfunc { local foo= foo="$(testCode)" || "{echo" "foo";} cat "$foo" || { badCode case $? in *) exit 1 esac } echo "Finished." ``` It's also interesting that this - in contrast to the original example - triggers a syntax error: ``` #!/bin/bash function badCode { echo "bad code executed" } function testCode { #pick some existing file echo "/etc/passwd" } function tfunc { local foo= foo="$(testCode)" || {echo "foo";} cat "$foo" || { badCode case $? in *) exit 1 esac } } #<-- only difference to the original example echo "Finished." ``` smime.p7s Description: S/MIME Cryptographic Signature
Re: Interesting bug
P.S.: Also, if you remove the case/esac from the original example, it'll result in a syntax error. So why can the case/esac be used to ignore the syntax error? smime.p7s Description: S/MIME Cryptographic Signature
Re: Interesting bug
On Feb 12 2022, David Hobach wrote: > Yes, the interesting part is that depending on which space you accidentally > forget, you'll either get the expected "Finished" or "bad code executed". > foo="$(testCode)" || {echo "foo"; } # --> bad code > foo="$(testCode)" || { echo "foo";} # --> Finished There is no forgotten space in the latter line. -- Andreas Schwab, sch...@linux-m68k.org GPG Key fingerprint = 7578 EB47 D4E5 4D69 2510 2552 DF73 E780 A9DA AEC1 "And now for something completely different."
Re: Interesting bug
Hi David! > A syntax error would be nice instead. I see - also from you second answer - that you have some fundamental misunderstandings about shell programming: - Your code will, in case the testCode fails, try execute a program called {echo. This is certainly not a good name for a program, but it is not a syntax error either. - Since bash interprets the code the problem with the closing brace is only detected when the offending line is passed. My personal opinion (although I'll probably be bashed here for it ;-) ): Except for a very few exceptions like small wrappers avoid writing new shell scripts at all - for example use python instead. There are many historically founded flaws and odds with shell proramming. Anyhow, bug-bash@gnu.org is not the right place for these topics, better try help-b...@gnu.org. Best regards Martin
Re: Interesting bug
On Sat, Feb 12, 2022 at 8:25 PM David Hobach wrote: > I guess it closes the function and {echo is interpreted as string or so, > but that is probably not all (testCode is e.g. never executed). > Let's see if I get this right. Removing the irrelevant parts, your code is pretty much this: func() { {echo hello;} { echo funny stuff exit 1 } } The key here is that '{' is a keyword, like 'if', or 'do', not an operator like '('. Which just might be quite different in other programming languages, but here we are. So, having '{echo' instead of '{' is a bit like having 'fiecho' instead of 'fi'. Not relevant for the syntax, so that the first '}' just after then ends the function and the block with "echo funny stuff" is on the top level. You could have this instead, to the same effect: func() { {echo hello } if true; then echo funny stuff exit 1 fi fi Without the explicit "exit", you'd get the syntax error after the "echo funny stuff" was run. The function itself is never called, so "echo hello", or whatever there is, never runs. On the other hand, the shell parses lines in full before doing anything (and it needs to look if there's a redirection after the block), so with '} }' or 'fi fi' on one line instead, the syntax error is caught before the block runs. For fun, try that with 'fi fi', 'fi; fi', 'fi; xyz' and 'fi xyz' and see which versions run the block (and try to figure out why)
Re: Interesting bug
Thanks a lot for the detailed explanations, much appreciated! So essentially it's no bug - just a rather uncommon choice of keywords. I still don't agree with that choice since it can be abused to somewhat hide code in pull requests to less experienced maintainers, but oh well... there's probably enough fun to be had with unicode et al already. And I guess the more modern tastes are relatively recent in the history of bash. smime.p7s Description: S/MIME Cryptographic Signature