I am trying to check out test by using the sytax with the brackets [ and ].
The manpage of test says that -n can be used to check if the length of string is not zero:
-n STRING
the length of STRING is nonzero
In the opposite -z can be used ti check if the length of a string is zero:
-z STRING
the length of STRING is zero
To understand how test works I crated an example with an empty variable foo which has the length of zero and a second variable bar with a length of one:
$ export foo
$ export bar=1
$ [ -n $bar ] ; echo $?
0
$ [ -n $foo ] ; echo $?
0
$ [ -z $bar ] ; echo $?
1
$ [ -z $foo ] ; echo $?
0
The first two tests with the -n are showing both zero as result. I suggested It would be zero for the first test and one for the second. The second test shows the result as suggested.
Try the following:
[ -n "$foo" ] ; echo $?
Since the $foo is an empty string, the
[ -n $foo ] # without quotes
becomes:
[ -n ]
Manual test(1) says:
STRING equivalent to -n STRING
So [ -n ] is treated not like [ -n "" ], but like [ "-n" ]
Related
I have a sample logic. By calculation, it is -2 > 0 . This is a false condition and I get no result, but I am getting the value as abcd.
#!/bin/bash
Val1=1
Val2=1
Val3=2
Name=abcd
log=/tmp/log
if [ "$((Val1-(Val2 + Val3))) -ge 0" ] ; then echo "$Name" | tee -a "$log"; fi
I have verified on ShellCheck, and it is giving me this message. I couldn't get how to fix this?
$ shellcheck myscript
Line 7:
if [ "$((Val1-(Val2 + Val3))) -ge 0" ] ; then echo "$Name" | tee -a "$log"; fi
^-- SC2157: Argument to implicit -n is always true due to literal strings.
If you use [ 'a string' ], that's the same as [ -n 'a string' ]: it checks if the parameter is non-empty. You're providing the argument "$((Val1-(Val2 + Va3))) -ge 0", which evaluates to the string "-2 -ge 0". "Implicit -n" means that the shell really checks
if [ -n "-2 -ge 0" ]
and that is always true, because that's not an empty string.
The reason is your quoting. This would work instead:
if [ "$((Val1-(Val2 + Val3)))" -ge 0 ]
Strictly speaking, the quotes aren't necessary, but they don't hurt, either.
Or, since you're using Bash, you could use an arithmetic conditional:
if ((Val1 - (Val2 + Val3) > 0))
I figured after posting the question
#!/bin/bash
Val1=1
Val2=1
Val3=2
Name=abcd
log=/tmp/log
if [ "$((Val1-(Val2 + Val3)))" -ge 0 ] ; then echo "$Name" | tee -a "$log"; fi
Usually i only use [[ for all kinds of test cases, because it's the most advanced way and it's more safe to use (Regex, ...).
I know that [[ executes different code than [, but according to the manpage and various documentations, it should at least handle options like "-n" the same way, but it doesn't.
-n STRING the length of STRING is nonzero
VAR=
if [[ -n $VAR ]]
then
echo "\$VAR is nonzero"
else
echo "\$VAR is zero"
fi
$VAR is zero
VAR=
if [ -n $VAR ]
then
echo "\$VAR is nonzero"
else
echo "\$VAR is zero"
fi
$VAR is nonzero
How is this even possible?
bash 4.1.2(1)
I think that your problem is related to quotes.
When you use [ -n $VAR ] the command that is executed won't contain any argument where $VAR should be:
$ set -x
$ [ -n $VAR ]
+ '[' -n ']'
This means that you are essentially testing whether the string -n is non-empty, because the following two tests are equivalent:
[ string ] # is a shorthand for
[ -n string ] # which is always true!
If you use quotes, then you get different behaviour:
$ [ -n "$VAR" ]
+ '[' -n '' ']'
Now you are testing whether the variable is non-empty, so you get the expected behaviour.
Quoting. You have to quote variables that you use in [:
$ VAR=
$ [ -n $VAR ]
$ echo $?
0
$ [ -n "$VAR" ]
$ echo $?
1
I found the following command:
var=""; [ -n $var ] && echo T || echo F
This command returns F.
In my understanding, $var is null so the command should return T.
I also checked this:
[ -n "" ] && echo T || echo F
This returns T as I expected.
I can not understand why
[ -n "" ] && echo T || echo F
returns T but
var="" [ -n $var ] && echo T || echo F
returns F?
I checked them on CentOS6.4 bash4.1.2
Executive summary: always quote your parameter expansions unless you know why it should not be. Use [ -n "$var" ] instead.
When unquoted,
[ -n $var ]
expands to
[ -n ]
that is, the empty string is removed from the input and bash is left with a one-argument call to [, which means the command returns true if the argument is non-empty. -n here is not an operator; it is simply a non-empty string.
You need to quote the parameter expansion so that
[ -n "$var" ]
expands to
[ -n "" ]
which is a two-argument call to [, which will do as you expect: test if the second argument has non-zero length.
This difference in output in case when you use a variable is that you don't quote the variable.
When you say:
[ -n $var ] && echo T || echo F
the shell reads it as:
'[' -n ']'
and returns T. This case is equivalent to saying [ foo ] which would always be true.
On the other hand, when you say:
[ -n "" ] && echo T || echo F
the shell would read it as:
'[' -n '' ']'
and return F.
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 ]]
}
function wtf() {
echo "\$*='$*'"
echo "\$#='$#'"
echo "\$#='"$#"'"
echo "\$#='""$#""'"
if [ -n "$*" ]; then echo " [ -n \$* ]"; else echo "![ -n \$* ]"; fi
if [ -z "$*" ]; then echo " [ -z \$* ]"; else echo "![ -z \$* ]"; fi
if [ -n "$#" ]; then echo " [ -n \$# ]"; else echo "![ -n \$# ]"; fi
if [ -z "$#" ]; then echo " [ -z \$# ]"; else echo "![ -z \$# ]"; fi
}
wtf
produces
$*=''
$#=''
$#=''
$#=''
![ -n $* ]
[ -z $* ]
[ -n $# ]
[ -z $# ]
though it seems to me that [-n $#] should be false because 7.3 Other Comparison Operators indicates that [ -n "$X" ] should be the inverse of [ -z "$X" ] for all $X.
-z
string is null, that is, has zero length
String='' # Zero-length ("null") string variable.
if [ -z "$String" ]
then
echo "\$String is null."
else
echo "\$String is NOT null."
fi # $String is null.
-n
string is not null.
The -n test requires that the string be quoted within the test brackets. Using an unquoted string with ! -z, or even just the unquoted string alone within test brackets (see Example 7-6) normally works, however, this is an unsafe practice. Always quote a tested string. [1]
I know $# is special but I did not know it was special enough to violate boolean negation. What is happening here?
$ bash -version | head -1
GNU bash, version 4.2.42(2)-release (i386-apple-darwin12.2.0)
The actual numeric exit codes are all 1 or 0 as per
$ [ -n "$#" ]; echo "$?"
0
When $# is empty, "$#" doesn't expand to an empty string; it is removed altogether. So your test is not
[ -n "" ]
but rather
[ -n ]
Now -n isn't an operator, but just a non-empty string, which always tests as true.
"$#" doesn't do what you expect. It's not a different form of "$*", it expands to the quoted list of arguments passed to the current script.
If there are no arguments, it expands to nothing. If there are two arguments a and b c, then it expands to "a" "b c" (i.e. it preserves whitespace in arguments) while "$*" expands to "a b c" and $* would expand to a b c (three words).