In a function Bash: how to check if an argument is a set variable?
Asked Answered
L

5

6

I want to implement a bash function which test is the 1st argument is actually a variable, defined somewhere.

For instance, in my .bashrc :

customPrompt='yes';
syntaxOn='no';
[...]
function my_func {
    [...]
    # I want to test if the string $1 is the name of a variable defined up above
    # so something like: 
    if [[ $$1 == 'yes' ]];then 
         echo "$1 is set to yes";
    else
         echo "$1 is not set or != to yes";
    fi
    # but of course $$1 doesn't work
}

output needed :

$ my_func customPrompt
> customPrompt is set to yes
$ my_func syntaxOn
> syntaxOn is set but != to yes
$ my_func foobar
> foobar is not set

I tried a lot of test, like -v "$1", -z "$1", -n "$1", but all of them test $1 as a string not as a variable. (please correct me if I make not myself clear enought)

Lobeline answered 24/7, 2014 at 9:15 Comment(0)
H
5

In the bash you can use the indirect variable subtituion.

t1=some
t2=yes
fufu() {
    case "${!1}" in
        yes) echo "$1: set to yes. Value: ${!1}";;
        '')  echo "$1: not set. Value: ${!1:-UNDEF}";;
        *)   echo "$1: set to something other than yes. Value: ${!1}";;
    esac
}

fufu t1
fufu t2
fufu t3

prints

t1: set to something other than yes. Value: some
t2: set to yes. Value: yes
t3: not set. Value: UNDEF

The ${!variablename} in bash mean indirect variable expansion. Described in the e.g. https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html

Whrere:

The basic form of parameter expansion is ${parameter}. The value of parameter is substituted. The braces are required when parameter is a positional parameter with more than one digit, or when parameter is followed by a character that is not to be interpreted as part of its name.

If the first character of parameter is an exclamation point (!), a level of variable indirection is introduced. Bash uses the value of the variable formed from the rest of parameter as the name of the variable; this variable is then expanded and that value is used in the rest of the substitution, rather than the value of parameter itself. This is known as indirect expansion. The exceptions to this are the expansions of ${!prefix } and ${!name[@]} described below. The exclamation point must immediately follow the left brace in order to introduce indirection.

Also, check this: https://mcmap.net/q/1774099/-bash-indirect-variable-assignment-inside-a-function how to modify in a function a value of the variable passed indirectly.

Honea answered 24/7, 2014 at 9:30 Comment(4)
I like the approach, great works ! Could you edit your answer to explain a bit (with a link maybe) what "${!VAR}" is ? I understand how it works, because that's exactly what i was looking for, but I didnt find it. :)Lobeline
@Ash_ added the basic bash link more you can find with "bash indirect variable expansion"Honea
Perfect. Your other answer on the linked Q is really interesting too ! Thanks mate.Lobeline
@Ash_ Enjoy, I'm glad to help. :)Honea
L
2

You can check variable set or not by simply like

if [[  $var ]]
then
    echo "Sorry First set variable"
else
    echo $var  
fi

You can do something like this for your script

customPrompt='yes';
syntaxOn='no';
function my_func 
{
      if [[ ${!1} ]];then 
          echo "$1 is set to ${!1}";
      else
         echo "$1 is not set";
      fi
}
my_func customPrompt
my_func syntaxOn
my_func foobar

Output:

customPrompt is set to yes
syntaxOn is set to no
foobar is not set

You can customize the function as per you requirement by simply making some comparison conditions.

For more details you can check this answer

Loon answered 24/7, 2014 at 9:24 Comment(0)
V
1

If you really want to check if your variable is set or unset (not just empty), use this format:

function my_func {
    if [[ -z ${!1+.} ]]; then 
         echo "$1 is not set."
    elif [[ ${!1} == yes ]]; then
         echo "$1 is set to yes"
    else
         echo "$1 is set to \"${!1}\"."
    fi
}
Very answered 24/7, 2014 at 10:52 Comment(0)
K
1

You're going to have problems...

The Bash shell is a very wily creature. Before you execute anything, Bash comes in and interpolates your command. Your command or shell script never sees whether or not you have a variable as a parameter.

$ set -x
set -x
$ foo=bar
+ foo=bar
$ echo "$foo"
+ echo bar
bar
$ set +x

The set -x turns on debugging mode in the shell. It shows you what a command actually executes. For example, I set foo=bar and then do echo $foo. My echo command doesn't see $foo. Instead, before echo executes, it interpolates $foo with bar. All echo sees at this point is that it's suppose to take bar as its argument (not $foo).

This is awesomely powerful. It means that your program doesn't have to sit there and interpret the command line. If you typed echo *.txt, echo doesn't have to expand *.txt because the shell has already done the dirty work.

For example, here's a test shell script:

#! /bin/sh
if [[ $1 = "*" ]]
then
    echo "The first argument was '*'"
else
    "I was passed in $# parameters"
fi

Now, I'll run my shell script:

$ test.sh *
I was passed in 24 parameters

What? Wasn't the first parameter of my script a *? No. The shell grabbed * and expanded it to be all of the files and directories in my directory. My shell script never saw the *. However, I can do this:

$ test.sh '*'
The first argument was '*'

The single quotes tell the shell not to interpolate anything. (Double quotes prevent globbing, but still allow for environment variable expansion).

This if I wanted to see if my first parameter is a variable, I have to pass it in single quotes:

$ test.sh '$foo'

And, I can do this as a test:

if [[ $1 != ${1#$} ]]
then
    echo "The first parameter is the variable '$1'"
fi

The ${1#$} looks a bit strange, but it's just ${var#pattern}. This removes pattern from the left most side of $var. I am taking $1 and removing the $ if it exists. This gets expanded in the shell as:

if [[ $foo != foo ]]

which is true.

So, several things:

  • First, you've got to stop the shell from interpolating your variable. That means you have to use single quotes around the name.
  • You have to use pattern matching to verify that the first parameter starts with a $.
  • Once you do that, you should be able to use your variable with ${$1} in your script.
Kirovograd answered 24/7, 2014 at 13:6 Comment(0)
T
0
$ declare -p VAR &>/dev/null || echo undefined
Tubing answered 3/6 at 12:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.