Bash declare/init variables from array using a reference - bash

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.

Related

Linux script persist environment variable

I am trying to persist keys fetched form key vault as environment variable for a given user on linux server. The script does not work. I am not even able to see the if the variable was set temporarily in the shell.
This is my script.
#!/usr/bin/env bash
KEY_VAULT=$1
function fetch_secret_from_keyvault() {
local SECRET_NAME=$1
az keyvault secret show --vault-name "${KEY_VAULT}" --name "${SECRET_NAME}" --query "value"
}
function store_secret_from_keyvault() {
local SECRET_VAR=$1
local SECRET_NAME=$2
local SECRET_VALUE=`fetch_secret_from_keyvault "${SECRET_NAME}"`
store_secret "${SECRET_VAR}" "${SECRET_VALUE}"
}
function store_secret() {
local SECRET_VAR=$1
local SECRET_VALUE=$2
echo "export ${SECRET_VAR}=${SECRET_VALUE}"
}
echo "# ----------------------- "
echo "# Fetched the following secret from ${KEY_VAULT} on "`date`
store_secret_from_keyvault "MONGO_URI" "local-dev-mongo-uri"
I have read that export only temporarily stores the variable.
The script runs, but the variables are not set at the end. I would like to see them when executing
printenv
Assumptions:
OP wants to dynamically populate and export a new variable such that ...
the new variable is available/exported in the current session
One idea using a nameref ...
function store_secret() {
declare -n SECRET_VAR=${1}
export SECRET_VAR=${2}
}
Running a test:
$ unset secret_var
$ secret_var=56
$ typeset -p secret_var
declare -- secret_var="56" # defined as a normal variable
$ unset secret_var
$ typeset -p secret_var
-bash: typeset: secret_var: not found # variable is undefined
$ store_secret secret_var 47
$ typeset -p secret_var
declare -x secret_var="47" # defined as an exported variable
If you run a script to set variables, the variables will only be set in the context of that particular execution. To set variables, you have to source the file, not execute it.
Ex. setenv.bash
#!/bin/bash
export var1=value1
export var2=value2
If you do ./setenv.bash, var1 and var2 will only exist while the script is running.
If you do . ./setenv.bash or source ./setenv.bash, var1 and var2 will exist after the script is done.

Dynamic variable created in function not available in future calls

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]}"
}

Can't access global associate array using indirect expansion?

I have the following setup:
#! /bin/bash
init_globals() {
declare -gA global_arr1=( ["key"]="val" )
}
init_globals
echo "${global_arr1["key"]}" # WORKS! print val
local_arr1=( ["key"]="local val" )
i=1
temp=local_arr$i
current_arr=${!temp}
echo ${current_arr["key"]} # WORKS! print local val
temp=global_arr$i
current_arr=${!temp}
echo ${current_arr["key"]} # DOESN'T WORK! expect val but print nothing...
I'm trying to access globally defined associative array, based on the variable i. So I use indirect expansion to assign current_arr to what I want. It works perfectly for an associative array defined locally. But it doesn't work with global array. Why so?
You didn't declare local_arr1 as an associative array. It springs into existence with
local_arr1=( [key]="local val" )
so bash creates a normal array for you, with key understood as a variable whose value is the index in the array (zero in this case, as there's no $key). You can test it with set -eu or key=1.
Note that the correct way to use indirection on arrays is to include the index in the string:
arr1[2]=x
i=1
j=2
tmp=arr$i[$j]
echo ${!tmp}
It is because:
local_arr1=( ["key"]="local val" )
is not really an associative array. You can check by:
declare -p local_arr1
which prints:
declare -a local_arr1='([0]="local val")'
If you use it right:
declare -A local_arr1=( ["key"]="local val" )
then behavior will be same for both arrays.

shell script associate array value overwriting

When I run the following shell script always I am getting the output as "grault" for any key.
What would be the problem?
thanks!
#!/bin/bash
declare -a MYMAP
MYMAP=( [foo]=bar [baz]=quux [corge]=grault )
echo ${MYMAP[foo]}
echo ${MYMAP[baz]}
Create an associative array with -A:
declare -A MYMAP
See: help declare
The other answer describes how to do it right, but here's the explanation of why your example behaves as it does.
declare -a creates an indexed array, which should only accept integers for the index. If you provide a string as the index, it will just disregard it and treat it as a 0! (I think this is a poor behavior, it should just give an error).
So this is what your code translated to:
declare -a MYMAP # create indexed array
MYMAP=( [0]=bar [0]=quux [0]=grault )
echo ${MYMAP[0]} # grault
echo ${MYMAP[0]} # grault

ksh: Defining a parameter name with another parameter's value

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.

Resources