[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