Shell script variable not empty (-z option) - shell

How to make sure a variable is not empty with the -z option ?
errorstatus="notnull"
if [ !-z $errorstatus ]
then
echo "string is not null"
fi
It returns the error :
./test: line 2: [: !-z: unary operator expected

Of course it does. After replacing the variable, it reads [ !-z ], which is not a valid [ command. Use double quotes, or [[.
if [ ! -z "$errorstatus" ]
if [[ ! -z $errorstatus ]]

Why would you use -z? To test if a string is non-empty, you typically use -n:
if test -n "$errorstatus"; then
echo errorstatus is not empty
fi

I think this is the syntax you are looking for:
if [ -z != $errorstatus ]
then
commands
else
commands
fi

if [ !-z $errorstatus ]
just put a space between ! and -z, like this:
if [ ! -z $errorstatus ]

Related

Difference between `if [ ! -z "$var" ] then` and `if [ "$var"] then" in bash

Is there any difference between
if [ ! -z "$var" ] then
# do smth
fi
and
if [ "$var" ] then
# do smth
fi
They both seem to check if variable is set
Yes, they're equivalent, but there are a couple of notes that apply to both of them:
You need either a semicolon or a line break between ] and the then keyword, or it'll misparse them weirdly.
They're not testing whether the variable is set, they're testing whether it's set to something other than the empty string (see this question for ways to check whether a variable is truly unset).
However, I actually prefer a third also-equivalent option:
if [ -n "$var" ]; then
I consider this semantically clearer, because the -n operator specifically checks for something being non-empty.
There's no difference between
[ ! -z "$var" ]
[ -n "$var" ]
[ "$var" ]
All of them are true if $var is not empty.

How to test multiple variables for empty string

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"

Nested condition of if-statement in bash [duplicate]

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.

Multiple `if` statements in bash script

I'm trying to write a short bash script that optionally accepts arguments from the command line, or prompts for their input
if [ [ -z "$message" ] && [ -z "$predefined" ] ] ; then
read -p "Enter message [$defaultMessage]: " message
message=${message:-$defaultMessage}
else
if [ -n "$predefined" ]; then
if [ -f $base/$environment/vle/data/$predefined.txt ]; then
echo Predefined message file $predefined.txt does not exist
exit 1
fi
fi
fi
If neither message nor predefined has been passed in as command line arguments, then the code should prompt for a value for message; otherwise if predefined has been passed in as a command line argument, then the script should test for the existence of a file of that name and only continue if the file does exist
But I'm getting the following error
[: -z: binary operator expected
at the first of those if tests
Any help in explaining what's wrong with my syntax for that first if statement? Or providing an alternative syntax to achieve the same objectives.
The first if is not well-formed. This would work:
if [ -z "$message" ] && [ -z "$predefined" ]; then
or this:
if test -z "$message" && test -z "$predefined"; then
or this bash-specific, easy but dirty way:
if [[ -z "$message" ]] && [[ -z "$predefined" ]]; then
or this bash-specific proper way:
if [[ -z $message && -z $predefined ]]; then
In this last version the double-quotes are unnecessary, not a typo.
Thanks #mklement0 for the corrections in the bash-specific style, and for this final comment:
I should note that there's one case where double-quoting is still a must inside [[ ... ]], namely if you want a variable reference on the right side of a string comparison (==) to be treated as a literal:
v='[a]'
[[ $v == $v ]] # FALSE!
[[ $v == "$v" ]] # true
Without double-quoting, the right-hand side is interpreted as a pattern. Some people advocate always double-quoting variable references so as not to have to remember such subtleties. That said (from bash 3.2 on), you must NOT double-quote the right operand when regex matching with =~
test expression1 -a expression2
is true if both expressions are true.
test expression1 -o expression2
is true if either or both expressions are true.
if [ -z "$message" -a -z "$predefined" ]; then
read -p "Enter message [$defaultMessage]: " message
message=${message:-$defaultMessage}
else
if [ -n "$predefined" -a -f $base/$environment/vle/data/$predefined.txt ]; then
echo Predefined message file $predefined.txt does not exist
exit 1
fi
fi
This was able to combine 4 test into 2 while also getting rid of one nested if expression; then ; fi

What's the difference between _string_ and -n _string_ in bash conditional expression?

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 ]]
}

Resources