4

I wonder why the test [[ ! -v 1 ]] fails no matter if I pass the 1st positional parameter to the function below:

shopt -os nounset

function foo {
  echo -n "$FUNCNAME: 1st positional parameter "
  [[ ! -v 1 ]] && echo "is missing." || echo is "\"$1\"."
}

I know there are other ways to test but why doesn't this particular test work?

fedorqui
  • 275,237
  • 103
  • 548
  • 598
Tim Friske
  • 2,012
  • 1
  • 18
  • 28
  • AFAIK, `-v` is not an operator that is available with `[[` (at least according to the man page that came with bash 4.1 on my system). Perhaps you want `[[ -z "$1" ]]` ? Or do you really need to test for the existence of zero-length positional parameters? – ghoti Nov 13 '12 at 13:45
  • Try `[[ -v SHELL ]]; echo $?`. `-z` doesn't work here because of `shopt -os nounset`. – Tim Friske Nov 13 '12 at 13:47
  • Possibly it doesn't work with integers, using normal variable names like "foo" works. – Nihathrael Nov 13 '12 at 13:48
  • @TimFriske `[[ -v SHELL ]]` returns `bash: conditional binary operator expected` and ``bash: syntax error near `SHELL'``. Ruh roh! – ghoti Nov 13 '12 at 13:51
  • It is in my bash 4.2 manpage: `-v varname True if the shell variable varname is set (has been assigned a value).` – dogbane Nov 13 '12 at 13:54
  • @ghoti: I don't receive any error. I'm on Fedora 17 64 bit with `echo $BASH_VERSION` giving me `4.2.39(1)-release`. – Tim Friske Nov 13 '12 at 13:54
  • Ah, there we go. The `-v` operator was added in Bash 4.2. I would be highly surprised if it was dependent on operating system, but I was testing in an older FreeBSD system with bash 4.1, and Mac OSX 10.6 which comes with bash 3.2. – ghoti Nov 13 '12 at 13:58
  • 5
    I think the positional parameters don't qualify as _shell variables_, that's why -v doesn't work on them – German Garcia Nov 13 '12 at 14:55
  • 1
    For me, the check for parameters existence should be done with `$#`. Also see http://stackoverflow.com/questions/11492338/idiomatic-way-to-test-if-no-positional-params-are-given – German Garcia Nov 13 '12 at 15:11

4 Answers4

4

In this case, you want to check if the parameter is unset.

has_1() {
  if [[ -z "${1+present}" ]]; then 
    echo "no first param"
  else
    echo "given: $1"
  fi
}

The parameter expansion ${var+word} will return "word" only if the parameter is not unset -- i.e. if you pass an empty string, the function will indicate the first parameter is given.

glenn jackman
  • 238,783
  • 38
  • 220
  • 352
  • Your proposal sounds reasonable because it references the positional parameter by its symbol and avoids `bash` from failing with `shopt -os nounset`. If nobody else can explain why `[[ ! -v 1 ]]` doesn't work I shall consider it as a **bug**. – Tim Friske Nov 13 '12 at 17:45
  • 1
    apparently, -v doesn't work with positional paramaters: `set -- bar; echo $1; [[ -v 1 ]] || echo no foo` – glenn jackman Nov 13 '12 at 18:04
  • 1
    from Bash 5.1 it does work: https://stackoverflow.com/a/65233119/1983854 (feel free to copy into your answer) – fedorqui Jan 12 '21 at 12:22
4

Bash 5.1 has a very straight-forward way to do this. As described in the release notes:

x. `test -v N' can now test whether or not positional parameter N is set.

So to check if the Nth parameter is set can use either of these:

test -v N
[ -v N ]

See a very basic example:

test -v 1 && echo "1st parameter is set: '$1'"
[ -v 2 ] && echo "2nd parameter is set: '$2'"

See some samples:

bash-5.1$ bash script.sh ""
1st parameter is set: ''

bash-5.1$ bash script.sh a b
1st parameter is set: 'a'
2nd parameter is set: 'b'

bash-5.1$ bash script.sh ""
1st parameter is set: ''

bash-5.1$ bash script.sh "" bla
1st parameter is set: ''
2nd parameter is set: 'bla'
fedorqui
  • 275,237
  • 103
  • 548
  • 598
3
if [ "$#" -gt "0" ]; then echo 'ok'; else echo '0'; fi
alinsoar
  • 15,386
  • 4
  • 57
  • 74
  • We already had this answer but it got deleted by the author because `$#` only indirectly tests whether the 1st positional parameter is set. Also in his answer he could not explain why `[[ ! -v 1 ]]` fails. – Tim Friske Nov 13 '12 at 17:51
  • Neither do I know what means the option `-v` to `test`. – alinsoar Nov 13 '12 at 18:06
1

The following pattern covers most of the practical use cases:

[[ -z "${2:-}" ]] && echo "The 2'nd parameter is unset" >&2 && exit 1

Shell-Parameter-Expansion

Dmitry
  • 664
  • 8
  • 8