With bash >5, I'm trying to assign a different value to variables depending on the architecture specified in a variable. I use a function to do so. This works perfectly:
# arguments:
variable name to assign,
value for mac arch,
value for pi arch
create_variable_for_arch() {
if [ "$_run_for_arch" = "mac" ]; then
eval $1=\$2
else
eval $1=\$3
fi
}
However, this breaks my script for some reason:
create_variable_for_arch() {
if [ "$_run_for_arch" = "mac" ]; then
declare "$1"="$2"
else
declare "$1"="$3"
fi
}
Here is a snippet to demonstrate how I use create_variable_from_arch()
declare _moonlight_opt_audio
declare _arch_specific_stream_command
#
while getopts "b:fahdr:s" options; do
case $options in
a)
create_variable_for_arch "_moonlight_opt_audio" \
"--audio-on-host" "-localaudio"
;;
esac
done
create_variable_for_arch "_moonlight_opt_fps" "--fps 60" "-fps 60"
start_streaming() {
_arch_specific_options="$_moonlight_opt_resolution $_moonlight_opt_fps $_moonlight_opt_audio $_moonlight_opt_display_type $_moonlight_opt_bitrate"
create_variable_for_arch "_arch_specific_stream_command" "$_arch_specific_options stream $_target_computer_ip $_moonlight_opt_app_name" "stream $_arch_specific_options -app $_moonlight_opt_app_name $_target_computer_ip"
moonlight $_arch_specific_stream_command
}
The trace looks like this with eval()
+ start_streaming
+ _arch_specific_options='--resolution 1920x1080 --fps 60 --bitrate 5000'
+ create_variable_for_arch _arch_specific_stream_command '--resolution 1920x1080 --fps 60 --bitrate 5000 stream 192.168.1.30 StreamMouse' 'stream --resolution 1920x1080 --fps 60 --bitrate 5000 -app StreamMouse 192.168.1.30'
+ '[' mac = mac ']'
+ eval '_arch_specific_stream_command=$2'
++ _arch_specific_stream_command='--resolution 1920x1080 --fps 60 --bitrate 5000 stream 192.168.1.30 StreamMouse'
+ moonlight --resolution 1920x1080 --fps 60 --bitrate 5000 stream 192.168.1.30 StreamMouse
moonlight --resolution 1920x1080 --fps 60 --bitrate 5000 stream 192.168.1.30 StreamMouse
But with declare it looks like this:
+ start_streaming
+ _arch_specific_options=
+ create_variable_for_arch _arch_specific_stream_command ' stream 192.168.1.30 ' 'stream -app 192.168.1.30'
+ '[' mac = mac ']'
+ declare '_arch_specific_stream_command= stream 192.168.1.30 '
+ echo moonlight
moonlight
$_arch_specific_options ends up with no value. What is going on? I've tried a few different ways of quoting or not quoting variables, but I don't really understand what's doing what in terms of quotations.
Best Answer
declare
(like thetypeset
of other shells; also understood bybash
as an alias fordeclare
) declares a variable in the current scope (and can set a type and/or value).So here, you would declare a variable that is local to the
create_variable_for_arch
function. When that function returns, that variable would be gone.bash
'sdeclare
/typeset
has a-g
option to declare the variable global), but you can't use that either as it declares the variable (and sets its type and/or value) in the outer-most scope as oppose to the scope of the caller of the function, so is pretty useless there (it's more useful inmksh
/zsh
/yash
where it's only skipping the making it local or with ksh93 which has static scoping, see What do `declare name` and `declare -g` do? for details).SO here, your options are either to use
eval
, or to use namerefs:Or, assuming
$_run_for_arch
is constant in your script:Or with namerefs:
It's often (rightly) recommended to avoid
eval
for security reasons, buteval
is safe when used properly.declare
andnamerefs
would be as unsafe here when used improperly, as they can both also evaluate code.All of:
Would run the
reboot
command if called with:It's important to make sure the first argument is a variable name to avoid the arbitrary command execution vulnerability.
would be much worse. As those parameter expansions are unquoted, they're subject to split+glob, so even the contents of
$2
can end up being evaluated as shell code, as in: