title: Why does the bash "test -n" command give the wrong result for the $@ (dollar at) positional parameter while "! test -z" works?
body: <p>Using <code>$@</code> inside a bash <code>test</code> expression gives odd results. </p>
  
  <p>Here's a minimal test script to reproduce the issue (among the alternative formulations for the same test which <em>do</em> pass without error):</p>
  
  <pre><code>#! /bin/bash
  # file path: ./bash_weirdness.sh
  
  echo "args: '$@'"
  
  echo "--- empty ---"
  
  if test "" ; then echo      "1.A1.TEST      - not empty?"; fi
  if test -z "" ; then echo   "1.A2.EMPTY     - empty?"; fi
  if test -n "" ; then echo   "1.A3.NE        - not empty?"; fi
  if ! test "" ; then echo    "1.B1.NOT.TEST  - empty?"; fi
  if ! test -z "" ; then echo "1.B2.NOT.EMPTY - not empty?"; fi
  if ! test -n "" ; then echo "1.B3.NOT.NE    - empty?"; fi
  
  echo "--- space ---"
  
  if test " " ; then echo      "2.A1.TEST      - not empty?"; fi
  if test -z " " ; then echo   "2.A2.EMPTY     - empty?"; fi
  if test -n " " ; then echo   "2.A3.NE        - not empty?"; fi
  if ! test " " ; then echo    "2.B1.NOT.TEST  - empty?"; fi
  if ! test -z " " ; then echo "2.B2.NOT.EMPTY - not empty?"; fi
  if ! test -n " " ; then echo "2.B3.NOT.NE    - empty?"; fi
  
  echo "--- \$@ ---"
  
  if test "$@" ; then echo      "3.A1.TEST      - not empty?"; fi
  if test -z "$@" ; then echo   "3.A2.EMPTY     - empty?"; fi
  if test -n "$@" ; then echo   "3.A3.NE        - not empty?"; fi
  if ! test "$@" ; then echo    "3.B1.NOT.TEST  - empty?"; fi
  if ! test -z "$@" ; then echo "3.B2.NOT.EMPTY - not empty?"; fi
  if ! test -n "$@" ; then echo "3.B3.NOT.NE    - empty?"; fi
  
  echo "--- \$* ---"
  
  if test "$*" ; then echo      "4.A1.TEST      - not empty?"; fi
  if test -z "$*" ; then echo   "4.A2.EMPTY     - empty?"; fi
  if test -n "$*" ; then echo   "4.A3.NE        - not empty?"; fi
  if ! test "$*" ; then echo    "4.B1.NOT.TEST  - empty?"; fi
  if ! test -z "$*" ; then echo "4.B2.NOT.EMPTY - not empty?"; fi
  if ! test -n "$*" ; then echo "4.B3.NOT.NE    - empty?"; fi
  </code></pre>
  
  <p>The first two sections ('empty' and 'space') are in there to verify that <code>test</code>, <code>test -n</code> and <code>test -z</code> work fine; that's how bad the confusion is.</p>
  
  <p>The problem occurs when <code>$@</code> represents 'no arguments', i.e. when <code>$#</code> would be zero(0).</p>
  
  <p>Three test runs show what's wrong: notice the '3.A3.NE - not empty?' report in the third run:</p>
  
  <pre><code>$ ./bash_weirdness.sh x
  -&gt;
  args: 'x'
  --- empty ---
  1.A2.EMPTY     - empty?
  1.B1.NOT.TEST  - empty?
  1.B3.NOT.NE    - empty?
  --- space ---
  2.A1.TEST      - not empty?
  2.A3.NE        - not empty?
  2.B2.NOT.EMPTY - not empty?
  --- $@ ---
  3.A1.TEST      - not empty?
  3.A3.NE        - not empty?
  3.B2.NOT.EMPTY - not empty?
  --- $* ---
  4.A1.TEST      - not empty?
  4.A3.NE        - not empty?
  4.B2.NOT.EMPTY - not empty?
  </code></pre>
  
  <p>Which is fine.</p>
  
  <pre><code>$ ./bash_weirdness.sh ""
  -&gt;
  args: ''
  --- empty ---
  1.A2.EMPTY     - empty?
  1.B1.NOT.TEST  - empty?
  1.B3.NOT.NE    - empty?
  --- space ---
  2.A1.TEST      - not empty?
  2.A3.NE        - not empty?
  2.B2.NOT.EMPTY - not empty?
  --- $@ ---
  3.A2.EMPTY     - empty?
  3.B1.NOT.TEST  - empty?
  3.B3.NOT.NE    - empty?
  --- $* ---
  4.A2.EMPTY     - empty?
  4.B1.NOT.TEST  - empty?
  4.B3.NOT.NE    - empty?
  </code></pre>
  
  <p>Which is fine too.</p>
  
  <pre><code>$ ./bash_weirdness.sh 
  -&gt;
  args: ''
  --- empty ---
  1.A2.EMPTY     - empty?
  1.B1.NOT.TEST  - empty?
  1.B3.NOT.NE    - empty?
  --- space ---
  2.A1.TEST      - not empty?
  2.A3.NE        - not empty?
  2.B2.NOT.EMPTY - not empty?
  --- $@ ---
  3.A2.EMPTY     - empty?
  3.A3.NE        - not empty?
  3.B1.NOT.TEST  - empty?
  --- $* ---
  4.A2.EMPTY     - empty?
  4.B1.NOT.TEST  - empty?
  4.B3.NOT.NE    - empty?
  </code></pre>
  
  <p>Which is <strong><em>not</em> fine at all</strong>: notice the incorrect '3.A3.NE' result.</p>