Use string as bash variable name in alternative value expansion [duplicate] - bash

This question already has answers here:
How can I look up a variable by name with #!/bin/sh (POSIX sh)?
(4 answers)
Closed 7 years ago.
How can I use the value of one variable as the name of another variable in an alternative value expansion (${var+alt}) in bash?
I would think that
#!/bin/bash
cat='dog'
varname='cat'
if [ -z ${`echo "${varname}"`+x} ]; then
echo 'is null'
fi
should be roughly equivalent to
#!/bin/bash
if [ -z ${dog+x} ]; then
echo 'is null'
fi
but when I try to do this, I get
${`echo "${cat}"`+x}: bad substitution
I guess part of the problem is that the subshell doing the command substitution doesn't know about $varname anymore? Do I need to export that variable?
My reason for doing this is that I learned from this answer how to check if a variable is null, and I'm trying to encapsulate that check in a function called is_null, like this:
function is_null {
if [ $# != 1 ]; then
echo "Error: is_null takes one argument"
exit
fi
# note: ${1+x} will be null if $1 is null, but "x" if $1 is not null
if [ -z ${`echo "${1}"`+x} ]; then
return 0
else
return 1
fi
}
if is_null 'some_flag'; then
echo 'Missing some_flag'
echo $usage
exit
fi

I'm not sure if I understand your problem.
If I got, what you need is eval command.
$ cat='dog'
$ varname='cat'
$ echo ${varname}
cat
$ eval echo \$${varname}
dog

Related

Check ENV variables in a loop to see if they were set [duplicate]

This question already has an answer here:
Bash indirect variable referencing
(1 answer)
Closed 5 months ago.
Thought it was a simple thing, but not sure why it got messed up
I'm trying in a for-loop to check a few ENV variables and make sure they were set.
This is the current code
# Bash 4.2 ( not 4.4 )
# Verify ENV
declare -a env_vars=(
"HOME",
"PATH",
"PYTHONPATH",
"TESTNONEXISTING"
)
for evar in "${env_vars[#]}"; do
# Tried: printenv
# Tried: -v $evar
# Tried: eval
if [[ -z "${evar}" ]]; then
echo
echo "ERROR: ENV var '$evar' is missing"
echo
exit 1
fi
done
I tried many things suggested here in StackOverflow - but they don't work when used in a loop ( as a string )
An example of something that works, but it's useless ...
required_env () {
ename=$1
evalue=$2
if [[ -z "$evalue" ]]; then
echo "ENV variable '$ename' is missing"
exit 1
else
echo "Variable exists"
fi
}
required_env "HOME" $HOME
required_env "PATH" $PATH
etc ...
The problem is mainly - how to convert a string - to a real variable inside the "if" - that's what I cannot get ..
Any suggestions ?
-z tests if the string is not empty. On the first iteration evar=HOME, sp [[ -z "${evar}" ]] becomes [[ -z "HOME" ]]. You are checking if the name of the variable is not empty, not the value of the variable.
You can just check if a variable is set with -v.
[[ -v "$evar" ]]
Note that there is a difference between unset and (set and empty) and (set and not-empty). -z checks for unset or (set and empty).
how to convert a string - to a real variable inside the "if"
Use variable indirection.
[[ -z "${!evar}" ]]

Loop Through Variables in bash and change their content [duplicate]

This question already has answers here:
Indirect variable assignment in bash
(7 answers)
Iterating over variable name in bash script
(2 answers)
What is indirect expansion? What does ${!var*} mean?
(6 answers)
Closed 5 months ago.
How can I make this into a loop? I have trouble with looping through variables.
WSTATUS="exited" //example output from command
VSTATUS="running"
NSTATUS="running"
JSTATUS="running"
if [[ $WSTATUS == run* ]]; then
WSTATUS=${GREEN}$WSTATUS
else
WSTATUS=${RED}$WSTATUS
fi
if [[ $VSTATUS == run* ]]; then
VSTATUS=${GREEN}$VSTATUS
else
VSTATUS=${RED}$VSTATUS
fi
if [[ $NSTATUS == run* ]]; then
NSTATUS=${GREEN}$NSTATUS
else
NSTATUS=${RED}$NSTATUS
fi
if [[ $JSTATUS == run* ]]; then
JSTATUS=${GREEN}$JSTATUS
else
JSTATUS=${RED}$JSTATUS
fi
I have tried this:
...varibles
array=( $WSTATUS $VSTATUS $NSTATUS $JSTATUS )
for value in "${array[#]}"
do
if [[ $value == run* ]]; then
WSTATUS=${GREEN}$value
else
WSTATUS=${RED}$value
fi
done
How can i iterate through bash variables, not their content?
changing this wstatus into value does not work --> WSTATUS=${GREEN}$value
Not a loop, but a function already helps a lot:
colorme() {
if [[ "$1" == run* ]]; then
printf '%s' "${GREEN}$1"
else
printf '%s' "${RED}$1"
fi
}
WSTATUS=$(colorme "$WSTATUS")
VSTATUS=$(colorme "$VSTATUS")
NSTATUS=$(colorme "$NSTATUS")
JSTATUS=$(colorme "$JSTATUS")
You can use nameref (declare -n var in following script)
#!/usr/bin/env bash
GREEN=GREEN
RED=RED
WSTATUS="exited"
VSTATUS="running"
NSTATUS="running"
JSTATUS="running"
array=( WSTATUS VSTATUS NSTATUS JSTATUS )
declare -n var
for var in "${array[#]}"
do
[[ $var == run* ]] && prefix="${GREEN}" || prefix="${RED}"
var="$prefix$var"
done
declare -p WSTATUS VSTATUS NSTATUS JSTATUS
What you are looking for is declare and ! expansion
a=b
declare $b=12
echo $b
=>
12
That is for setting a variable whose name is computed (here from another variable)
echo ${!a}
=>
12
That is for accessing the content of a variable whose name is stored in another variable
So in your case, it may look like
WSTATUS="exited" //example output from command
VSTATUS="running"
NSTATUS="running"
JSTATUS="running"
totest=( WSTATUS VSTATUS NSTATUS JSTATUS )
for name in ${array[#]}
do
if [[ ${!name} == run* ]]; then
declare $name=${GREEN}${!name}
else
declare $name=${RED}${!name}
fi
done
Copying without any other modification your script. I have some reservation about the idea to add those, apparently green and red escape code to the variable content, rather than taking this decision at print time. I didn't really try do understand nor the test neither the action your script is taking. Even, (XY problem parenthesis) the fact that what you need is to loop through variable is questionable. knittl solution, tho it does not answer to your question, is probably a better solution to your real problem.
But well, your question was how to loop through variable. This is a way to do it.

bash scription if conditional "${1:-}" [duplicate]

This question already has answers here:
Usage of :- (colon dash) in bash
(2 answers)
Closed 1 year ago.
I try to understant the if condition
log_daemon_msg () {
if [ -z "${1:-}" ]; then
return 1
fi
log_daemon_msg_pre "$#"
if [ -z "${2:-}" ]; then
echo -n "$1:" || true
return
fi
echo -n "$1: $2" || true
log_daemon_msg_post "$#"
}
what is mean "${1:-}" and "${2:-}"
See Shell Parameter Expansion, {parameter:-word}.
${parameter:-word}
If parameter is unset or null, the expansion of word is substituted. Otherwise, the value of parameter is substituted.
So ${1:-} is the first argument of the function log_daemon_msg or the empty string if the function was called without arguments or with an empty first argument.
Normally, that doesn't really make sense, as just writing $1 would have the same effect. However, if your script runs with set -u (exit when using an undefined variable) ${1:-} can be used to get the standard-behavior ($1 turns into the empty string if unset). But the echo -n "$1: $2" at the end would still fail in the case of missing arguments.

Shell always evaluates both factors of &&? [duplicate]

This question already has answers here:
Why does 'test -n' return 'true' in bash?
(2 answers)
Closed 2 years ago.
When $thing is null this quits...
thing=`command_that_could_return_null`
echo "thing is" $thing
if [ -z $thing ]; then exit; fi
...but this...
if [ -n $thing ] && [ $thing = "special_value" ]; then
do_the_special_thing
fi
comes out with
[: =: unexpected operator
I don't understad.
When $thing is null the first factor of the && evaluates to false and therefore the second should not be evaluated, but it appears that it is being evaluated?
I guess it's just a feature of sh that I have to write this as two ifs?
If you fix your quote problem (which #CharlesDuffy mentioned) - your bottom 3 lines work just fine:
if [ -n "$thing" ] && [ "$thing" = "special_value" ]; then
do_the_special_thing
fi
Also, you referred to variables being "null", but the relevant term is a variable that is "unset". In Bourne shell terminology, "null" refers to the empty string.

How to echo (print) the value of a variable in Bash?

I am trying to write a little script, and I can not figure out how to choose the variable to be echo'ed (echo $'TEST'_"$response") dynamically depending on the user's input:
#!/usr/bin/env sh
response=response
TEST_1="Hi from 1!"
TEST_2="Hi from 2!"
while [ $response ]; do
read -p "Enter a choice between 1 - 2, or 'bye': " response
if [ $response = 'bye' ]; then
echo "See You !"; exit
elif [ $response -ge 1 ] && [ $response -le 2 ]; then
echo $'TEST'_"$response"
else
echo "Input is not a valid value."
fi
done
The desired output would be the value of one of the variables declared at the beginning of my script ("Hi from 1!" or "Hi from 2!"). Instead my script simple outputs the name of the variable as a string "TEST_1" or "TEST_2". I do not simply want to hardcode the variable that will be printed like:
if [ $response -ge 1 ]; then
echo $TEST_1
fi
since it is not scalable. Using backticks like
echo `$'TEST'_"$response"`
doesn't help either since bash will expect to run the result "TEST_1" or "TEST_2" as a command.
Any hint will be greatly appreciated.
You need indirect expansion, to be used with ${!var}:
$ TEST1="hello"
$ TEST2="bye"
$ v=1
$ var="TEST$v" #prepare the variable name of variable
$ echo ${!var} #interpret it
hello
$ v=2
$ var="TEST$v" #the same with v=2
$ echo ${!var}
bye
That is, you need to use a variable name of a variable and this is done with the indirect expansion: you use a variable with the name of the variable and then you evaluate it with the ${!var} syntax.
In your case, use:
myvar="TEST$response"
echo "${!myvar}"
Always use quotes, such as "$string", for anything other than numbers. For numbers, just keep it normal (i.e. $number).

Resources