I have a ksh script that reads a profile script with a number of sessions defined. Each session defines its own parameters as such:
SESSION_ONE_USER=...
SESSION_ONE_PWD=...
SESSION_TWO_USER=...
...
The script gets the SESSION parameter from the command line, but I simply set it for the example.
I want to let the SESSION parameter value define part of another parameter name, that I need the value from, like:
SESSION="SESSION_ONE"
USER=${${SESSION}_USER}
PASS=${${SESSION}_PWD}
That gives me a compile error.
I also tried
GET_USER_PARAM(){
echo ${SESSION}_USER
}
echo $`GET_USER_PARAM`
But that returns $SESSION_ONE_USER
I want it to return the value of the parameter SESSION_ONE_USER instead.
Does anyone have any solutions?
This is what eval is for:
SESSION=SESSION_ONE
eval echo \$${SESSION}_USER
should display the value of $SESSION_ONE_USER.
Don't monkey with variable names, use associative arrays instead
typeset -A users
typeset -A pwd
session=SESSION_ONE
users[$session]=joe
pwd[$session]=secret
for key in "${!users[#]}"; do
echo "user for session $key is ${users[$key]}"
echo "pwd for session $key is ${pwd[$key]}"
done
Try this:
SESSION="SESSION_ONE"
SESSION_ONE_USER="foo"
SESSION_ONE_PWD="bar"
SESSION_USER=${SESSION}_USER
SESSION_PWD=${SESSION}_PWD
USER=${!SESSION_USER}
PASS=${!SESSION_PWD}
echo $USER
echo $PASS
The "!" does a level of indirection. See Shell Parameter Expansion.
If this is ksh, then this is a job for nameref
alias nameref='typeset -n'
Example Solution
function session_parameters { set -u
typeset session=${1:?session name}
nameref user=SESSION_${session}_USER
nameref pass=SESSION_${session}_PASS
print session=$session user=$user pass=$pass
}
SESSION_ONE_USER="User1"
SESSION_ONE_PASS="Pass1"
SESSION_TWO_USER="User2"
SESSION_TWO_PASS="Pass2"
for s in ONE TWO THREE; do
session_parameters $s
done
Sample output
session=ONE user=User1 pass=Pass1
session=TWO user=User2 pass=Pass2
test_session_parameters[12]: session_parameters: line 5:
SESSION_THREE_USER: parameter not set
Note the usage of set -u to force the error message on line 3.
nameref usage: (from the builtin help text)
NAME
typeset - declare or display variables with attributes
SYNOPSIS
typeset [ options ] [name[=value]...]
-n
Name reference.
The value is the name of a variable that name references. name cannot contain a ... Cannot be use with any other options.
Related
Is it possible to first pass a parameter to the function and than change its value?
#!/bin/bash
name=old_name
echo $name #echoes "old_name"
alter () {
$1=new_name #throws error that says 'command not found'
}
alter name
echo $name #I would like to see "new_name" here
Yes, using a nameref:
alter () {
declare -n foo=$1
foo=new_name
}
See Bash FAQ 006 for more advice and warnings, as well as workarounds for versions of bash that predate nameref support (i.e., 4.2 or earlier).
You can't do it that way, unfortunately. Also, bash functions can't return values. The usual options are (1) set a global var in the function (yuck), or (2) echo the "return" value and use command substitution to call it. Something like this:
alter () {
echo "$1 but different"
}
name="Fred"
name=$(alter $name)
echo Name is now $name
returns: Name is now Fred but different
I have a script that is (supposed to be) assigning a dynamic variable name (s1, s2, s3, ...) to a directory path:
savedir() {
declare -i n=1
sn=s$n
while test "${!sn}" != ""; do
n=$n+1
sn=s$n
done
declare $sn=$PWD
echo "SAVED ($sn): ${!sn}"
}
The idea is that the user is in a directory they'd like to recall later on and can save it to a shell variable by typing 'savedir'. It -does- in fact write out the echo statement successfully: if I'm in the directory /home/mrjones and type 'savedir', the script returns:
SAVED (s1): /home/mrjones
...and I can further type:
echo $sn
and the script returns:
s1
...but typing either...
> echo $s1
...or
echo ${!sn}
...both return nothing (empty strings). What I want, in case it's not obvious, is this:
echo $s1
/home/mrjones
Any help is greatly appreciated! [apologies for the formatting...]
To set a variable using a name stored in another variable I use printf -v, in this example:
printf -v "$sn" '%s' "$PWD"
declare here is creating a variable local to the function, which doesn't seem to be what you want. Quoting from help declare:
When used in a function, declare makes NAMEs local, as with the local
command. The -g option suppresses this behavior.
so you can either try the -g or with the printf
Use an array instead.
savedir() {
s+=("$PWD")
echo "SAVED (s[$((${#s[#]}-1))]): ${s[${#s[#]}-1]}"
}
I got two variables in a bash script. One contains the name of a function within the script while the other one is an array containing KEY=VALUE or KEY='VALUE WITH SPACES' pairs. They are the result of parsing a specific file, and I can't change this.
What I want to do is to invoke the function whose name I got. This is quite simple:
# get the value for the function
myfunc="some_function"
# invoke the function whose name is stored in $myfunc
$myfunc
Consider the function foo be defined as
function foo
{
echo "MYVAR: $MYVAR"
echo "MYVAR2: $MYVAR2"
}
If I get the variables
funcname="foo"
declare -a funcenv=(MYVAR=test "MYVAR2='test2 test3'")
How would I use them to call foo with the pairs of funcenv being added to the environment? A (non-variable) invocation would look like
MYVAR=test MYVAR2='tes2 test3' foo
I tried to script it like
"${funcenv[#]}" "$funcname"
But this leads to an error (MYVAR=test: command not found).
How do I properly call the function with the arguments of the array put in its environment (I do not want to export them, they should just be available for the invoked function)?
You can do like this:
declare -a funcenv=(MYVAR=test "MYVAR2='test2 test3'")
for pairs in "${funcenv[#]}"; do
eval "$pairs"
done
"$funcname"
Note however that the variables will be visible outside the function too.
If you want to avoid that, then you can wrap all the above in a (...) subshell.
why don't you pass them as arguments to your function?
function f() { echo "first: $1"; echo "second: $2"; }
fn=f; $fn oneword "two words"
The following code snippet will try to initialize the variables in the arrVAR_INIT array :
#!/bin/bash
set -u
declare -a arrVAR_INIT=(
VERBOSE=FALSE
DEBUG=FALSE
MEMORY="1024k"
DEBUGFILE=
)
# declare -A arrVAR_DEFAULT_VALUE
for VAR in "${arrVAR_INIT[#]}"
do
VAR_NAME=${VAR%%=*}
VAR_VALUE="${VAR#*=}"
echo "$VAR : $VAR_NAME = \"$VAR_VALUE\""
#### ERROR: !VAR_NAME: unbound variable
declare $VAR_NAME="$VAR_VALUE"
# eval "arrVAR_DEFAULT_VALUE[${VAR%%=*}]=\"${VAR#*=}\""
done
Please note that, by using the set -u ( treat unset variables as an error, and immediately exit ), the above code will throw the !VAR_NAME: unbound variable error and exit.
What would be the correct way to init the vars though the reference ?
Can it be done without using eval ?
The quick answer is :
declare "$VAR_NAME=$VAR_VALUE"
Know that if you cannot guarantee the content of the variables is safe, this could open code injection vulnerabilities.
Is there a reason you are not using an associative array? You already have an array to start with, why not make it associative and read from it rather than initializing other variables?
declare -A arrVAR_INIT=(
[VERBOSE]=FALSE
[DEBUG]=FALSE
[MEMORY]="1024k"
[DEBUGFILE]=
)
echo "${arrVAR_INIT[VERBOSE]}" # An example of getting a value out of the array.
You can use declare $var_name="$var_value" like this:
#!/bin/bash
set -u
declare -a arrvar_init=(
VERBOSE=FALSE
DEBUG=FALSE
MEMORY="1024k"
DEBUGFILE=
)
# declare -A arrVAR_DEFAULT_VALUE
for var in "${arrvar_init[#]}"
do
var_name=${var%%=*}
var_value=${var#*=}
declare $var_name="$var_value"
declare -p $var_name
done
Avoid using all uppercase names for your variable names to avoid clash with bash ENV variables.
I want to save the variable name and its contents easily from my script.
Currently :-
LOGFILE=/root/log.txt
TEST=/file/path
echo "TEST : ${TEST}" >> ${LOGFILE}
Desired :-
LOGFILE=/root/log.txt
function save()
{
echo "$1 : $1" >> ${LOGFILE}
}
TEST=/file/path
save TEST
Obviously the above save function just saves TEST : TEST
Want I want it to save is TEST : /file/path
Can this be done? How? Many thanks in advance!
You want to use Variable Indirection. Also, don't use the function keyword, it is not POSIX and also not necessary as long as you have () at the end of your function name.
LOGFILE=/root/log.txt
save()
{
echo "$1 : ${!1}" >> ${LOGFILE}
}
TEST=/file/path
save TEST
Proof of Concept
$ TEST=foo; save(){ echo "$1 : ${!1}"; }; save TEST
TEST : foo
Yes, using indirect expansion:
echo "$1 : ${!1}"
Quoting from Bash reference manual:
The basic form of parameter expansion is ${parameter} [...] 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
Consider using the printenv function. It does exactly what it says on the tin, prints your environment. It can also take parameters
$ printenv
SSH_AGENT_PID=2068
TERM=xterm
SHELL=/bin/bash
LANG=en_US.UTF-8
HISTCONTROL=ignoreboth
...etc
You could do printenv and then grep for any vars you know you have defined and be done in two lines, such as:
$printenv | grep "VARNAME1\|VARNAME2"
VARNAME1=foo
VARNAME2=bar