Variable substitution when sourcing file - bash

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.

Related

Shell script to print value of variable recursively

#!/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

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

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.)

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

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)
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://stackoverflow.com/a/16131829/632407 how to modify in a function a value of the variable passed indirectly.
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
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
}
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.

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