Reputation: 1297
What I want to do is following. Inside a function, I need to assign a value to a variable, whose name is taken from another variable. In other words:
func() {
#
# Here happens something that ultimately makes $arg="var_name"
#
declare -g ${arg}=5
}
func
echo ${var_name}; # Prints out "5"
The code snippet above works great in bash 4.2. However, in bash before 4.2, declare
doesn't have the -g
option. Everything I found at google says that to define the global variable inside a function, I should just use the var=value
syntax, but unfortunately var
itself depends on another variable. ${arg}=5
doesn't work, either. (It says -bash: var_name=5: command not found
.
For the curious, the reason for all this is that this function actually creates global variables from the script parameters, i.e. running script --arg1=val
automatically creates variable named arg1
with value val
. Saves tons of a boilerplate code.
Upvotes: 32
Views: 53682
Reputation: 121
-g -r
does the job. My bash version is: 5.1.16. Here is an example:
function setup() {
declare -g -r VERSION="${1:-2.0}"
}
function main() {
setup "$@"
VERSION="2.3" # error
}
main "$@"
Upvotes: 0
Reputation: 51
The existence of the local
and declare
builtin commands may render it counter-intuitive that the wanted functionality is actually available from the export
builtin, at least on Bash.
According to the Bash manual, the export
command has an -n
option, which "causes the export property to be removed from each name". This also applies while declaring variables with values right ahead.
The following snippet demonstrates the usage:
VAR=old-value
function func () {
export -n "$1=$2"
}
func VAR new-value
echo "VAR=$VAR" # prints VAR=new-value
One may create one own's global
function, and use it inside other functions:
VAR=old-value
function global () {
export -n "$1=$2"
}
function main () {
global VAR "$1"
}
main main-value
echo "VAR=$VAR" # prints VAR=main-value
This is verified working on
I used this to inspect the various possibilities:
#!env bash
export LANG=C LC_ALL=C
A=a0
B=b0
C=c0
D=d0
E=e0
export F=f0
function testfunc () {
local "$1=$2" ; shift 2
declare "$1=$2" ; shift 2
declare -g "$1=$2" ; shift 2
export -n "$1=$2" ; shift 2
export "$1=$2"
}
echo "$A/$B/$C/$D/$E"
testfunc A a1 B b1 C c1 D d1 E e1
echo "$A/$B/$C/$D/$E"
export -p | grep -E '^declare -x [ABCDEF]='
In case of macOS 10.15 this shows an output similar to this:
$ bash test.sh
a0/b0/c0/d0/e0
test.sh: line 15: declare: -g: invalid option
declare: usage: declare [-afFirtx] [-p] [name[=value] ...]
a0/b0/c0/d1/e1
declare -x E="e1"
declare -x F="f0"
The output shows that the local
and declare
commands used in the testfunc
function effectively do not apply the variables in the global context (as documented) and that the declare -g
option is not widely supported yet. It also shows that the variables D and E have been changed in the global context from inside the testfunc
function (as hoped for), while only the E variable has been marked for export as a side effect by the export
command, showing that the export -n
option is effective. The variable F is there just to verify the grep command worked as expected.
Upvotes: 5
Reputation: 464
In Bash for declaring array variables inside a function definition you can use the local
command in combination with the eval
command like in the following example:
#!/bin/bash
function test
{
local -g $1
local array=( AA BB 'C C' DD)
eval ${1}='("${array[@]}")'
}
test VAR
echo VAR=${VAR[@]:1:2}
the output will be:
$./test.sh
VAR=BB C C
works with: GNU bash, Version 4.4.18
Upvotes: 3
Reputation: 148
If you want something a little less hackish, try using export
in place of declare -g
. It has the added benefit of being an environment variable now.
func() {
#
# Here happens something that ultimately makes $arg="var_name"
#
export ${arg}=5
}
func
echo ${var_name}; # Prints out "5"
Unfortunately, this still does not work for arrays.
Upvotes: 4
Reputation: 1295
Maybe your could use the following functions to dynamically assign your global variable in bash(< 4.2). If > 2 args passed, then the value will be array type. E.G
_set2globals variable_name arg1 [arg2] [arg3] [...]
Source:
# dynamically set $variable_name($1)=$values($2...) to globals scope
function _set2globals()
{
if (( $# < 2 )); then
printf "$FUNCNAME: expect at least 2 argument, but %d you given.\n" $# >&2
exit 1
fi
local ___pattern_='^[_a-zA-Z][_0-9a-zA-Z]*$'
if [[ ! $1 =~ $___pattern_ ]]; then
printf "$FUNCNAME: invalid variable name: %s.\n" "$1" >&2
exit 1
fi
local __variable__name__=$1
shift
local ___v_
local ___values_=()
while (($# > 0)); do
___v_=\'${1//"'"/"'\''"}\'
___values_=("${___values_[@]}" "$___v_") # push to array
shift
done
eval $__variable__name__=\("${___values_[@]}"\)
}
Upvotes: 0
Reputation: 46903
I think you need the printf
builtin with its -v
switch:
func() {
printf -v "$var" '5'
}
var=var_name
func
echo "$var_name"
will output 5
.
Upvotes: 10
Reputation: 13
Eval will work with Array variables...I just had to single quote the local Array variable inside the eval statement.
The below reads in a file (1st arg) a line at a time and then copies it to the variable name passed to the 2nd arg of the get_file.
get_file () {
declare -a Array=();
while read line; do
Array=("${Array[@]}" "($line)")
done < ${1}
eval ${2}='("${Array[@]}")'
unset Array
}
declare -a my_array=();
get_file /etc/myfile.conf my_array
echo ${#my_array[@]}
Upvotes: 0
Reputation: 191
declare
inside a function doesn't work as expected.
I needed read-only global variables declared in a function.
I tried this inside a function but it didn't work:
declare -r MY_VAR=1
But this didn't work. Using the readonly
command did:
func() {
readonly MY_VAR=1
}
func
echo $MY_VAR
MY_VAR=2
This will print 1 and give the error "MY_VAR: readonly variable" for the second assignment.
Upvotes: 19
Reputation: 115
Using "eval" (or any direct assignment) can give you headaches with special characters (or issues with value injection etc).
I've had great success just using "read"
$ function assign_global() {
> local arg="$1"
> IFS="" read -d "" $arg <<<"$2"
>}
$ assign_global MYVAR 23
echo $MYVAR
23
$ assign_global MYVAR "\"'"
"'
Upvotes: 5
Reputation: 4806
You could construct your var=value as a string and evaluate it using the bash builtin command eval
.
Upvotes: 4
Reputation: 212634
Since this is tagged shell
, it is important to note that declare
is a valid keyword only in a limited set of shells, so whether it supports -g is moot. To do this sort of thing in a generic Bourne shell, you can just use eval:
eval ${arg}=5
Upvotes: 8