This question already has answers here:
Meaning of "[: too many arguments" error from if [] (square brackets)
(6 answers)
Closed 4 years ago.
I want to check that of two variables both or neither are set. I've tried multiple options, and this is the most clean solution I've come up with:
if [ ! -z $A -a -z $B ] || [ -z $A -a ! -z $B ]; then
#error
fi
#success
When I run the script with both A and B set - it runs fine. But when I run it with A missing I get:
./test.sh: line 3: [: too many arguments
./test.sh: line 3: [: too many arguments
line 3 being the condition statement.
When I run it with B missing, I get:
./test.sh: line 3: [: argument expected
./test.sh: line 3: [: argument expected
Is it my condition that has wrong syntax or am I missing smth else?
You should try to avoid -a; it's non-standard and considered obsolete by the POSIX standard. Since || and && have equal precedence, you need to use { ... } to properly group the individual tests.
(This is in addition to the immediate need to quote your parameter expansions.)
if { [ ! -z "$A" ] && [ -z "$B" ]; } || { [ -z "$A" ] && [ ! -z "$B" ]; }; then
However, a simpler expression might be
if [ -z "$A$B" ] || { [ "$A" ] && [ "$B" ]; }; then
The concatenation of two strings is empty if and only if both strings are also empty.
[ "$A" ] is short for [ -n "$A" ], which is equivalent to [ ! -z "$A" ].
Using bash's [[ ... ]] command, you can write the more natural
if [[ -z $A && -n $B || -n $A && -z $B ]];
Quotes are optional in this case, and || and && are usable inside [[ ... ]] with the precedence you expect.
Quote your variables:
if [ ! -z "$A" -a -z "$B" ] || [ -z "$A" -a ! -z "$B" ]; then
If the variables are unquoted and unset, they are replaced with nothing, meaning that the command essentially becomes:
if [ ! -z -a -z ] || [ -z -a ! -z ]; then
resulting in the error you see.
You forgot to use quotation marks around your vars:
if [ ! -z "$A" -a -z "$B" ] || [ -z "$A" -a ! -z "$B" ]; then
echo "error"
fi
Bash will replace your vars in your script with the values, so when A=5 and B is unset, your version will read:
if [ ! -z 5 -a -z ] || [ -z 5 -a ! -z ]; then
You see that the syntax is wrong, as -z expects an argument. When using quotes, is reads:
if [ ! -z "5" -a -z "" ] || [ -z "5" -a ! -z "" ]; then
AS you can see, now the argument for B is an empty string, which is valid.
Also your version would have failed when setting A="string with spaces" when unquoted.
Related
I want to test multiple variables to see if they are empty. So I did the following:
if [ test -z "$VAR1" ] || [ test -z "$VAR2" ]
then
echo "Empty!"
fi
However, it doesn't work. The output is:
[: -z: binary operator expected
[: -z: binary operator expected
What have I done wrong? The code above works fine if I leave out the OR (||) condition.
Use either brackets or the test command, don't use both. Either of the following will work:
if [ -z "$VAR1" ] || [ -z "$VAR2" ]
then
echo "Empty!"
fi
Or:
if test -z "$VAR1" || test -z "$VAR2"
then
echo "Empty!"
fi
In some older shells [ was just an alias for the test command. You could even miss out the closing ] (or add it after test) and everything would be fine. Nowadays, on bash this will give a syntax error. However, the two (correct) syntaxes are still functionally equivalent and can be used interchangeably but not at the same time.
You should either use test, or [; they are synonymous, except [ requires a ] as last argument.
[ -z "$VAR1" ] || [ -z "$VAR2" ]
or
test -z "$VAR1" || test -z "$VAR2"
This question already has answers here:
Is there a list of 'if' switches anywhere?
(5 answers)
Closed 4 years ago.
What is is meaning of -a -z in
if [ -z "$ENV_VAR" -a -z "$ENV_VAR2"]; then
...
fi
bash conditional?
The first -z checks if $ENV_VAR defined according to
-z string True if the length of string is zero.
What does -a -z combination test with relation to ENV_VAR2?
according to the docs
-a file True if file exists.
however, ENV_VAR2 may contain text only, not a file name.
[ -z "$ENV_VAR" -a -z "$ENV_VAR2" ] has 2 conditions ANDed together using -a switch:
What it means is this:
-z "$ENV_VAR": $ENV_VAR is empty
-a: and
-z "$ENV_VAR2": $ENV_VAR2 is empty
btw if you're using bash you can refactor this condition to make it bit more succinct:
[[ -z $ENV_VAR && -z $ENV_VAR2 ]]
Please try this "man test".
Ideally, in that output, you'll see that -a performs an "AND" between two expressions.
It's "and".
See man test
EXPRESSION1 -a EXPRESSION2
both EXPRESSION1 and EXPRESSION2 are true
Examples:
$ [ -z "" -a -z "" ] && echo Hello
Hello
$ [[ -z "" -a -z "" ]] && echo Hello
bash: syntax error in conditional expression
bash: syntax error near `-a'
If used with single [ it is the "and" from test. If used with [[ it is the file check from bash.
The bash solution:
$ [[ -z "" && -z "" ]] && echo Hello
Hello
For POSIX compatibility, [[ ... && ... ]] is not available, but -a is considered obsolete (and optional) by POSIX, so use two separate [ commands instead.
if [ -z "$ENV_VAR" ] && [ -z "$ENV_VAR2" ]; then
...
fi
I'm learning BASH through HackerRank.There's an exercise in which the lengths of the triangle is given and then you need to find whether the triangle is isosceles,scalene or equilateral.I wrote the following code:
read a
read b
read c
if [ [ "$a" -eq "$b" ] && [ "$b" -eq "$c" ] ]
then
echo "EQUILATERAL"
elif [ [ "$a" -eq "$b" ] || [ "$b" -eq "$c" ] ]
then
echo "ISOSCELES"
else
echo "SCALENE"
fi
But then I get the following error
solution.sh: line 4: [: too many arguments
solution.sh: line 7: [: too many arguments
solution.sh: line 7: [: too many arguments
Why is this happening? I tried long and hard to rectify it but nothing worked out
You can combine conditions either ommiting the surrounding brackets like this
if [ "$a" -eq "$b" ] && [ "$b" -eq "$c" ]
or by combining the conditions with -a/-o like this
if [ "$a" -eq "$b" -a "$b" -eq "$c" ]
see http://wiki.bash-hackers.org/commands/classictest#and_and_or
&& and || are Bash list operators. In a chain of commands, the next command is executed only if the previous command returned 0 (&&) or nonzero (||).
[ is an alias for the Bash internal test command and has arguments such as -eq or -ne. ] ends its command line. Type help test for more information.
So if you write a conditional expression, you do not put the list operators inside brackets.
Try, for example, this instead of the respective line in your code:
if [ "$a" -eq "$b" ] && [ "$b" -eq "$c" ]
then
[ isn't a grouping operator in bash, you can't use it to group tests.
there are a number of different ways to express the tests you want to make, numeric evaluation mode is probably easiest to read
if (( a == b && b == c ))
if (( a == b || b == c || c == a ))
This is going to break if you have decimal fractions, but will work fine for integers.
[ is a conditional command, like an alias for sh's test built-in command.
[[ is the same for bash which has more test options.
So make a choice between [ and [[ but not [ [ which means two command.
Example:
# [ [ -n 'test' ] ]
bash: [: too many arguments
# [ -n 'test' ] && echo $?
0
# [[ -n 'test' ]] && echo $?
0
Suppose I am writing the following in a bash script:
if [ -z $a ] || [ -z $b ] ; then
usage
fi
It works but I would like to write it with short-circuiting as follows:
[ -z $a ] || [ -z $b ] || usage
Unfortunately it does not work. What am I missing ?
You want to execute usage in case either 1st or 2nd condition are accomplished. For that, you can do:
[ -z $a ] || [ -z $b ] && usage
Test:
$ [ -z "$a" ] || [ -z "$b" ] && echo "yes"
yes
$ b="a"
$ [ -z "$a" ] || [ -z "$b" ] && echo "yes"
yes
$ a="a"
$ [ -z "$a" ] || [ -z "$b" ] && echo "yes"
$
You could make use of the following form:
[[ expression ]]
and say:
[[ -z "$a" || -z "$b" ]] && usage
This would execute usage if either a or b is empty.
Always quote your variables. Saying
[ -z $a ]
if the variable a is set to foo bar would return an error:
bash: [: foo: binary operator expected
The Bash Reference Manual says that
[ string ]
and
[ -n string ]
will both return true if the string 's length is not 0
but the fact is not as so
greet=
if [ $greet ]; then
echo '1'
else
echo '2'
fi
if [ -n $greet ]; then
echo '1'
else
echo '2'
fi
the output is
2
1
the Bash Reference Manual just says
-n string
string
True if the length of string is non-zero.
so, what the real difference between the two form?
As #user1502952 said, you need to use double-quotes; but let me explain why. Suppose you execute:
greet=
[ -n $greet ] && echo "it's nonblank"
When the shell parses the [ -n $greet ] part, it expands $greet to the empty string, and then does word splitting. For instance, if $greet expanded to something with spaces in the middle, it would treat each "word" as a separate argument to the [ command. In this case, however, $greet expands to nothing, which contains no "word"s at all, and hence is treated as zero arguments to [ -- it effectively vanishes from the command. So [ -n $greet ] is equivalent to [ -n ], which checks to see if the string "-n" is nonblank. It is, so it evaluates to true.
Compare this with [ -n "$greet" ]: in this case, the double-quotes allow the expansion of $greet, but prevent word splitting. So the [ command actually gets a zero-length second argument, realizes that -n is supposed to be an operator, and gets the expected answer.
when you are using -n option, it is required to use double quotes.
if [ -n "$greet" ]
as the string is empty the above expression evaluates to false, as the length is zero.
if [ "$greet" ]
this also evaluates to false as the string is empty.
Moreover to check for empty string, -z option can be used.
if [ -z "$greet" ]
this will be true as the string is empty.
Check this link too: http://tldp.org/LDP/abs/html/comparison-ops.html
Bash performs word splitting inside [ but not inside [[, so you don't have to quote parameters if you use [[:
$ x=
$ [ -n $x ]; echo $?; [ -n "$x" ]; echo $?
0
1
$ [[ -n $x ]]; echo $?; [[ -n "$x" ]]; echo $?
1
1
type shows [[ $x ]] as [[ -n $x ]]:
$ f() { [[ $x ]]; }; type f
f is a function
f ()
{
[[ -n $x ]]
}