Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are the bash -n and -z test operators not inverses for $@

Tags:

operators

bash

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
like image 774
Mike Samuel Avatar asked Mar 04 '13 16:03

Mike Samuel


People also ask

What does $@ do bash?

bash [filename] runs the commands saved in a file. $@ refers to all of a shell script's command-line arguments. $1 , $2 , etc., refer to the first command-line argument, the second command-line argument, etc.

What does $() mean bash?

Again, $() is a command substitution which means that it “reassigns the output of a command or even multiple commands; it literally plugs the command output into another context” (Source).

What is Echo $$ in bash?

The echo command is used to display a line of text that is passed in as an argument. This is a bash command that is mostly used in shell scripts to output status to the screen or to a file.

What does $# mean in bash?

$# is the number of positional parameters passed to the script, shell, or shell function. This is because, while a shell function is running, the positional parameters are temporarily replaced with the arguments to the function. This lets functions accept and use their own positional parameters.


2 Answers

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.

like image 164
chepner Avatar answered Oct 24 '22 08:10

chepner


"$@" 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).

like image 35
Aaron Digulla Avatar answered Oct 24 '22 08:10

Aaron Digulla