bug-bash
[Top][All Lists]
Advanced

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

Re: Bash test builtin fails on: test -n '>' -a 1 -eq 1


From: Emanuele Torre
Subject: Re: Bash test builtin fails on: test -n '>' -a 1 -eq 1
Date: Tue, 22 Oct 2024 12:23:14 +0200
User-agent: Mutt/2.2.13 (2024-03-09)

On Tue, Oct 22, 2024 at 03:34:43PM +0800, cirrus.mazurka-0t@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



reply via email to

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