Shell script to print value of variable recursively - bash

#!/bin/bash
A="X"
X="Y"
B=${$A}
echo $B # expecting output to be 'Y'
Actual output seen : line 4: ${$A}: bad substitution

It's called parameter indirection:
You can use ${!nameref} to treat the value of nameref as a parameter:
A="X"
X="Y"
B=${!A}
echo "$B"
See https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html for more info on parameter expansion

Related

How can I override a bash variable at the time of executing it in console? [duplicate]

I have a whole bunch of tests on variables in a bash (3.00) shell script where if the variable is not set, then it assigns a default, e.g.:
if [ -z "${VARIABLE}" ]; then
FOO='default'
else
FOO=${VARIABLE}
fi
I seem to recall there's some syntax to doing this in one line, something resembling a ternary operator, e.g.:
FOO=${ ${VARIABLE} : 'default' }
(though I know that won't work...)
Am I crazy, or does something like that exist?
Very close to what you posted, actually. You can use something called Bash parameter expansion to accomplish this.
To get the assigned value, or default if it's missing:
FOO="${VARIABLE:-default}" # If variable not set or null, use default.
# If VARIABLE was unset or null, it still is after this (no assignment done).
Or to assign default to VARIABLE at the same time:
FOO="${VARIABLE:=default}" # If variable not set or null, set it to default.
For command line arguments:
VARIABLE="${1:-$DEFAULTVALUE}"
which assigns to VARIABLE the value of the 1st argument passed to the script or the value of DEFAULTVALUE if no such argument was passed. Quoting prevents globbing and word splitting.
If the variable is same, then
: "${VARIABLE:=DEFAULT_VALUE}"
assigns DEFAULT_VALUE to VARIABLE if not defined.
The colon builtin (:) ensures the variable result is not executed
The double quotes (") prevent globbing and word splitting.
Also see Section 3.5.3, Shell Parameter Expansion, in the Bash manual.
To answer your question and on all variable substitutions
echo "${var}"
echo "Substitute the value of var."
echo "${var:-word}"
echo "If var is null or unset, word is substituted for var. The value of var does not change."
echo "${var:=word}"
echo "If var is null or unset, var is set to the value of word."
echo "${var:?message}"
echo "If var is null or unset, message is printed to standard error. This checks that variables are set correctly."
echo "${var:+word}"
echo "If var is set, word is substituted for var. The value of var does not change."
You can escape the whole expression by putting a \ between the dollar sign and the rest of the expression.
echo "$\{var}"
Even you can use like default value the value of another variable
having a file defvalue.sh
#!/bin/bash
variable1=$1
variable2=${2:-$variable1}
echo $variable1
echo $variable2
run ./defvalue.sh first-value second-value output
first-value
second-value
and run ./defvalue.sh first-value output
first-value
first-value
see here under 3.5.3(shell parameter expansion)
so in your case
${VARIABLE:-default}
FWIW, you can provide an error message like so:
USERNAME=${1:?"Specify a username"}
This displays a message like this and exits with code 1:
./myscript.sh
./myscript.sh: line 2: 1: Specify a username
A more complete example of everything:
#!/bin/bash
ACTION=${1:?"Specify 'action' as argv[1]"}
DIRNAME=${2:-$PWD}
OUTPUT_DIR=${3:-${HOMEDIR:-"/tmp"}}
echo "$ACTION"
echo "$DIRNAME"
echo "$OUTPUT_DIR"
Output:
$ ./script.sh foo
foo
/path/to/pwd
/tmp
$ export HOMEDIR=/home/myuser
$ ./script.sh foo
foo
/path/to/pwd
/home/myuser
$ACTION takes the value of the first argument, and exits if empty
$DIRNAME is the 2nd argument, and defaults to the current directory
$OUTPUT_DIR is the 3rd argument, or $HOMEDIR (if defined), else, /tmp. This works on OS X, but I'm not positive that it's portable.
Then there's the way of expressing your 'if' construct more tersely:
FOO='default'
[ -n "${VARIABLE}" ] && FOO=${VARIABLE}
It is possible to chain default values like so:
DOCKER_LABEL=${GIT_TAG:-${GIT_COMMIT_AND_DATE:-latest}}
i.e. if $GIT_TAG doesnt exist, take $GIT_COMMIT_AND_DATE - if this doesnt exist, take "latest"
Here is an example
#!/bin/bash
default='default_value'
value=${1:-$default}
echo "value: [$value]"
save this as script.sh and make it executable.
run it without params
./script.sh
> value: [default_value]
run it with param
./script.sh my_value
> value: [my_value]
If you want 1 liner for your if-then-else, then we can consider rewriting:
if [ -z "${VARIABLE}" ]; then
FOO='default'
else
FOO=${VARIABLE}
fi
with semicolons:
if [ -z ${VARIABLE} ]; then FOO=`default`; else FOO=${VARIABLE}; fi
Alternatively, you can drop the if-then-else-fi keywords if you use boolean operators such as:
[ -z "${VARIABLE}" ] && FOO='default' || FOO=${VARIABLE}
Generalizing, the boolean operator pattern is:
conditional && then_command || else_command

Variable substitution when sourcing file

suppose we are sourcing a file with 1000 variables:
var1=50
....
var1000=100
how can i expand these variables so that if i define an argument matching var1...var1000 as a string,it will display their values? for example:
#!/bin/bash
source file
var3=$1 #assume that $1=var1
# [ variable substitution here ]
if [ -z $var3 ];then
echo "variable value is: ... " <-i want value of 50 to be printed
else
echo "variable does not exist"
fi
# (if $1=var1 or $1=var2)
Thanks!
Don't really understand the question but assume you want to do something like this.
In that you want to check the values in the sourced file.
The below uses bash indirect expansion:
The basic form of parameter expansion is ${parameter}.
[...]
If the first character of parameter is an exclamation point (!), it introduces a level of variable indirection. 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.
#!/bin/bash
source file
var3="$1" #assume that $1=var1
if [[ -n ${!var3} ]];then
#Check if the variable that has been sent to script has value in sourced file
echo "variable value is: ${!var3}"
#If it does echo the value
else
echo "variable does not have value"
#if it doesn't then state it has no value
fi
Usage
Assuming var1=50 is in file and there is no var20 in file then below is an example of the usage.
$ ./script.sh var1
variable value is: 50
$ ./script.sh var20
variable does not have value
In the if condition, use $((var3)) instead of $var3. Your issue would be solved.

How to test if a variable exists and has been initialized

I have to execute a function which has to test if a variable has been correctly defined in Bash and must use its associated value.
For instance, these variables are initialized at the top of the script.
#!/bin/bash
var1_ID=0x04
var2_ID=0x05
var3_ID=0x06
var4_ID=0x09
I would like to call the script named test as follows:
./test var1
The current implemented function is:
function Get()
{
if [ $1"_ID" != "" ]; then
echo "here"
echo $(($1_ID))
else
exit 0
fi
}
I don't understand why I obtain here even if I enter ./test toto or something else.
Do I need to use a specific command, such as grep?
Use parameter expansion:
: ${var:?}
Remove the colon if the empty string is a valid value (i.e., you only want to test for definedness).
: ${var?}
If you don't want the script to stop on the problem, you can use
if [[ ${var:+1} ]] ; then
# OK...
else
echo Variable empty or not defined. >&2
fi
Documented under Parameter Expansion in man bash:
When not performing substring expansion, using the forms documented below (e.g., :-), bash
tests for a parameter that is unset or null. Omitting the colon results in a test only
for a parameter that is unset.
${parameter:?word}
Display Error if Null or Unset. If parameter is null or unset, the expansion of
word (or a message to that effect if word is not present) is written to the standard error and the shell, if it is not interactive, exits. Otherwise, the value of
parameter is substituted.
${parameter:+word}
Use Alternate Value. If parameter is null or unset, nothing is substituted, otherwise the expansion of word is substituted.
You probably want to use indirect expansion: ${!variable} and then -n to check if it has been defined:
The indirect expansion consists in calling a variable with another variable. That is, as the variable name may be changing, instead of saying $a we say ${!var} and var=a.
$ cat a
var1_ID=0x04
var2_ID=0x05
var3_ID=0x06
var4_ID=0x09
for i in {1..5}; do
v="var${i}_ID"
if [ -n "${!v}" ]; then # <-- this expands to varX_ID
echo "$v set to value: ${!v}"
else
echo "$v not set"
fi
done
If we execute, we get:
$ ./a
var1_ID set to value: 0x04
var2_ID set to value: 0x05
var3_ID set to value: 0x06
var4_ID set to value: 0x09
var5_ID not set
From man test:
-n STRING
the length of STRING is nonzero
In bash 4.2, you can use the -v operator in a conditional expression to test if a variable with the given name is set.
if [[ -v ${1}_ID ]]; then
echo "${1}_ID is set"
foo=${1}_ID
echo "${!foo}"
fi
You still need indirect parameter expansion to get the value.
In bash 4.3 you can use a named reference to make working with it easier.
declare -n param=${1}_ID
if [[ -v param ]]; then
echo "${1}_ID"
echo "$param"
fi
(param will behave exactly like the variable it references. I don't know if there is an easy way, short of parsing the output of declare -p param, to get the name of the variable it references.)

BASH - Positional Parameters

I'm wondering if this could be possible:
scriptname: testing
#! /bin/bash
i=2
arg=`echo "$"$i`
echo $arg #value should be the value of $2 and not just '$2' string
echo $2
exit 0
command: testing a b
output
$2
b
Is there a way to make the value of $arg equal to the value of $2 which is "b" instead of just displaying the string "$2" aside from just directly assigning the value of $2 to $arg, arg=$2?
Tried doing this arg=echo ${$i} but I get this error: testing: ${$i}: bad substitution
Thanks in advance
Yes; you can use indirect expansion, which looks like this:
i=2
arg="${!i}" # equivalent to: arg="$2"
See the fourth paragraph of ยง3.5.3 "Shell Parameter Expansion" in the Bash Reference Manual.

Assigning default values to shell variables with a single command in bash

I have a whole bunch of tests on variables in a bash (3.00) shell script where if the variable is not set, then it assigns a default, e.g.:
if [ -z "${VARIABLE}" ]; then
FOO='default'
else
FOO=${VARIABLE}
fi
I seem to recall there's some syntax to doing this in one line, something resembling a ternary operator, e.g.:
FOO=${ ${VARIABLE} : 'default' }
(though I know that won't work...)
Am I crazy, or does something like that exist?
Very close to what you posted, actually. You can use something called Bash parameter expansion to accomplish this.
To get the assigned value, or default if it's missing:
FOO="${VARIABLE:-default}" # If variable not set or null, use default.
# If VARIABLE was unset or null, it still is after this (no assignment done).
Or to assign default to VARIABLE at the same time:
FOO="${VARIABLE:=default}" # If variable not set or null, set it to default.
For command line arguments:
VARIABLE="${1:-$DEFAULTVALUE}"
which assigns to VARIABLE the value of the 1st argument passed to the script or the value of DEFAULTVALUE if no such argument was passed. Quoting prevents globbing and word splitting.
If the variable is same, then
: "${VARIABLE:=DEFAULT_VALUE}"
assigns DEFAULT_VALUE to VARIABLE if not defined.
The colon builtin (:) ensures the variable result is not executed
The double quotes (") prevent globbing and word splitting.
Also see Section 3.5.3, Shell Parameter Expansion, in the Bash manual.
To answer your question and on all variable substitutions
echo "${var}"
echo "Substitute the value of var."
echo "${var:-word}"
echo "If var is null or unset, word is substituted for var. The value of var does not change."
echo "${var:=word}"
echo "If var is null or unset, var is set to the value of word."
echo "${var:?message}"
echo "If var is null or unset, message is printed to standard error. This checks that variables are set correctly."
echo "${var:+word}"
echo "If var is set, word is substituted for var. The value of var does not change."
You can escape the whole expression by putting a \ between the dollar sign and the rest of the expression.
echo "$\{var}"
Even you can use like default value the value of another variable
having a file defvalue.sh
#!/bin/bash
variable1=$1
variable2=${2:-$variable1}
echo $variable1
echo $variable2
run ./defvalue.sh first-value second-value output
first-value
second-value
and run ./defvalue.sh first-value output
first-value
first-value
see here under 3.5.3(shell parameter expansion)
so in your case
${VARIABLE:-default}
FWIW, you can provide an error message like so:
USERNAME=${1:?"Specify a username"}
This displays a message like this and exits with code 1:
./myscript.sh
./myscript.sh: line 2: 1: Specify a username
A more complete example of everything:
#!/bin/bash
ACTION=${1:?"Specify 'action' as argv[1]"}
DIRNAME=${2:-$PWD}
OUTPUT_DIR=${3:-${HOMEDIR:-"/tmp"}}
echo "$ACTION"
echo "$DIRNAME"
echo "$OUTPUT_DIR"
Output:
$ ./script.sh foo
foo
/path/to/pwd
/tmp
$ export HOMEDIR=/home/myuser
$ ./script.sh foo
foo
/path/to/pwd
/home/myuser
$ACTION takes the value of the first argument, and exits if empty
$DIRNAME is the 2nd argument, and defaults to the current directory
$OUTPUT_DIR is the 3rd argument, or $HOMEDIR (if defined), else, /tmp. This works on OS X, but I'm not positive that it's portable.
Then there's the way of expressing your 'if' construct more tersely:
FOO='default'
[ -n "${VARIABLE}" ] && FOO=${VARIABLE}
It is possible to chain default values like so:
DOCKER_LABEL=${GIT_TAG:-${GIT_COMMIT_AND_DATE:-latest}}
i.e. if $GIT_TAG doesnt exist, take $GIT_COMMIT_AND_DATE - if this doesnt exist, take "latest"
Here is an example
#!/bin/bash
default='default_value'
value=${1:-$default}
echo "value: [$value]"
save this as script.sh and make it executable.
run it without params
./script.sh
> value: [default_value]
run it with param
./script.sh my_value
> value: [my_value]
If you want 1 liner for your if-then-else, then we can consider rewriting:
if [ -z "${VARIABLE}" ]; then
FOO='default'
else
FOO=${VARIABLE}
fi
with semicolons:
if [ -z ${VARIABLE} ]; then FOO=`default`; else FOO=${VARIABLE}; fi
Alternatively, you can drop the if-then-else-fi keywords if you use boolean operators such as:
[ -z "${VARIABLE}" ] && FOO='default' || FOO=${VARIABLE}
Generalizing, the boolean operator pattern is:
conditional && then_command || else_command

Resources