2

From the bash manual

-v varname

True if the shell variable varname is set (has been assigned a value).

I test if positional parameter 1 is set by -v 1 in a bash script, but it doesn't let the test pass when I provide a command line argument. I wonder why?

#! /bin/bash

if [ -v 1 ]; then
    echo "1 exists"
fi

Thanks.

ilkkachu
  • 138,973
Tim
  • 101,790

1 Answers1

6

In Bash, it does work since Bash 5.1, but in earlier versions it just silently fails. (It also works in zsh.)

$ ./bash-5.1/bash -c 'set -- aa bb; for i in 1 2 3; 
                      do [ -v $i ] && echo y || echo n; done'
y
y
n

The CHANGES file in the source mentions this change for 5.1-alpha:

3. New Features in Bash
...
x. `test -v N' can now test whether or not positional parameter N is set.

Anyway, there's a bit of a hair-splitting difference between variables and parameters.

Bash's reference manual defines them like this:

A parameter is an entity that stores values. It can be a name, a number, or one of the special characters listed below. A variable is a parameter denoted by a name.

And a name is defined as

A word consisting solely of letters, numbers, and underscores, and beginning with a letter or underscore. Names are used as shell variable and function names. Also referred to as an identifier.

So all variables are parameters, but not all parameters are variables. And test -vvarname returns

True if the shell variable varname is set

So, technically speaking, it's not even documented to work for anything but real variables. Even if it now works for the positional parameters too, the documentation still appears to only mention variables.


In any case, we can use the "alternate value" parameter expansion ${1+x} and a test for an empty string for $1 and any other parameter (including any variables.) ${par+value} expands to the given string value if the parameter is set (even if it's set but empty), and otherwise it expands to the empty string.

$ bash -c 'set -- aa bb; [ ${1+x} ] && echo y || echo n'
y

This should work in all versions of Bash, and in any standard shell.

ilkkachu
  • 138,973
  • Thanks. Does [ $x ] test fail to pass when either the parameter is not set or its value is ""? – Tim Jan 27 '18 at 17:07
  • 1
    @Tim, [ ${param+x} ] tests for a set parameter, [ ${param:+x} ] tests for a set parameter with a nonempty value (but that's not nearly as useful, since you could just use [ -n "$param" ] in that case). Bash's manual mentions that a bit in passing, but the standard has a nice table of all those. – ilkkachu Jan 27 '18 at 17:12
  • In zsh the situation is clearer in that the positional parameter are also available in the $argv array variables (like in csh) and like in csh, zsh arrays are proper arrays. So 1 is like arg[1]. And you can do 1=foo like you can do argv[1]=foo. – Stéphane Chazelas Feb 08 '24 at 20:13