On Tue, Oct 22, 2024 at 03:34:43PM +0800, cirrus.mazurka...@icloud.com wrote: > The following produces a `bash: test: too many arguments` exception in > `test`, with exit status 2: > > ``` bash > v='>' > test -n "$v" -a yes '!=' no # bash: test: too many arguments > echo $? # 2 > > test -n '>' -a 1 -eq 1 # bash: test: too many arguments > echo $? # 2 > > [ -n '>' -a 1 -eq 1 ] # bash: [: too many arguments > echo $? # 2 > This is not a bug, it is just interpreting it as
[ '(' "-n" '>' "-a" ')' ... ] i.e. does "-n" come after "-a" lexicographically, and the 1 after that is a syntax error. Something like [ -n '>' -a -a 1 -eq 1 ] is not a syntax error. i.e. [ '(' "-n" '>' "-a" ')' -a '(' 1 -eq 1 ')' ] That is how that test expression is parsed by bash. The behaviour of test and [ in sh is always unspecified when there are more than 4 arguments, also -a and -o as logical operators have been marked obsolete and optional for years, and have finally been removed completely from the specification with the latest edition of POSIX Issue 8 2024 edition released this spring. You should just use && and || with multiple test commands instead, or use bash's special [[ ]] or (( )) syntaxes instead of using sh test/[. > [[ -n '>' -a 1 -eq 1 ]] > # bash: syntax error in conditional expression > # bash: syntax error near `-a' That is just because -a and -o as logical operators don't exist at all for [[. -a only exists as an alias for -e in [[. Use && and || instead. [[ -n '>' && 1 -eq 1 ]] Also note that in [[ -eq ]] and [ -eq ] are different: [-eq interprets strings as decimal numbers and compares them, and [[-eq interprets strings as arithmetic expressions like in $(( (so they can also run code, don't use [[-eq to compare user input), and then compares the results: $ [ 1 -eq " 1" ] && echo yes yes $ [[ 1 -eq " 1" ]] && echo yes yes $ x=10; [ 2 -eq x/5 ] && echo yes bash: [: x/5: integer expression expected $ x=10; [[ 2 -eq x/5 ]] && echo yes yes If you are writing a portable script also be careful when passing user input to [-eq/-lt/-le/-gt/-ge/-ne because some shells e.g. ksh93 have the [[ behaviour also for [-eq, and use it to run code. It is rare you actually need -eq or -ne, you can just use = and != instead. > echo $? # 2 > ``` > > It works without the -a, and as such, it works using && instead of the -a: > > ``` > v='>' > test -n "$v" > echo $? # 0 > > [ -n "$v" ] > echo $? # 0 > > test -n "$v" && test yes '!=' no > echo $? # 0 > > [ -n "$v" ] && [ yes '!=' no ] > echo $? # 0 > ``` > > There is no mention of this peculiar behaviour inside the documentation: > https://www.gnu.org/software/bash/manual/html_node/Bourne-Shell-Builtins.html#index-test > > Known versions affected: > GNU bash, version 5.2.37(1)-release (x86_64-apple-darwin22.6.0) > GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin22) > > If this is intended behaviour, then what is the suitable workaround? > 1. Should I always use `&&` instead of `-a`? > 2. Should I use a parameter replacement? e.g. `test -n "${v//>/.}" -a 1 -eq 1` > 3. Is there another suggestion? > > I've cross-posted this on Stack Exchange: > https://unix.stackexchange.com/q/785456/50703 > > Regards, Benjamin Lupton o/ emanuele6