How do you append to an indirect parameter expansion of an array in BASH?
#!/bin/bash
var1="target"
var2="arrayname"
targetarrayname=( "one" "two" "three" )
builtarrayname="${var1}${var2}[@]"
echo ${!builtarrayname} # prints "one two three"
eval "${builtarrayname:0:-3}+=( 'foo' )"
echo ${!builtarrayname} # prints "one two three foo"
Do note though that eval could be considered evil :p
You need to be sure that you only have sanitized input to eval statements, to avoid the possibility of unintended code execution.
EDIT:
The :0:-3
in eval "${builtarrayname:0:-3}+=( 'foo' )"
removes [@]
from the literal string that $builtarrayname
contains. Since assignment of arrays only use the variable name, we had to remove it. ( read more here to see how string manipulation in variables work )
and no, I don't think it can be done without eval, as that's how the left side of the assignment gets resolved before the assignment occurs (see this nice answer for more info on eval.
Indirect parameter expansion of an array argument in Bash
You can do it, but in an unexpected way: the placeholder var needs to include the array index:
multi_fruit() {
(( $# != 1 )) && return 1
tmp="${1}[@]"
for i in "${!tmp}"; do
echo "$i"
done
}
Also, it's a bad idea to use only uppercase variable names. One day you'll accidentally overwrite PATH and wonder why your script is broken. Leave uppercase vars to the system.
Also note that putting braces around the variable is not the same as double quotes. For example, consider:
var="one two three"
printf "%s\n" ${var}
printf "%s\n" "$var"
Bash indirect array addressing?
I've already found a resolution, this can be done by:
$ Aref=A$J
$ echo ${!Aref}
building
$ Aref=A$J[1]
$ echo ${!Aref}
blocks
$ Aref=A$J[@]
$ echo "${!Aref}"
building blocks
Bash parameter expansion, indirect reference, and backgrounding
The problem is that read
, when run in the background, isn't connected to a standard in.[details] Consider this simplified, working example with comment how to cripple it:
VALUES=( VALUE_A VALUE_B )
for value in "${VALUES[@]}"; do
read ${value} < <(echo ${RANDOM}) # add "&" and it stops working
done
echo "VALUE_A=${VALUE_A}"
echo "VALUE_B=${VALUE_B}"
You might be able to do this with coproc
, or using read -u
with automatic file descriptor allocation, but really this is a job for temporary files:
tmpdir=$(mktemp -d)
VALUES=( VALUE_A VALUE_B )
for value in "${VALUES[@]}"; do
(sleep 1; echo ${RANDOM} > "${tmpdir}"/"${value}") &
done
for value in "${VALUES[@]}"; do
wait_file "${tmpdir}"/"${value}" && {
read -r ${value} < "${tmpdir}"/"${value}";
}
done
echo "VALUE_A=${VALUE_A}"
echo "VALUE_B=${VALUE_B}"
rm -r "${tmpdir}"
This example uses wait_file
helper, but you might use inotifywait
if you don't mind some dependencies on OS.
How to iterate over an array using indirect reference?
${!ARRAYNAME[@]}
means "the indices of ARRAYNAME
". As stated in the bash man page since ARRAYNAME
is set, but as a string, not an array, it returns 0
.
Here's a solution using eval
.
#!/usr/bin/env bash
ARRAYNAME='FRUITS'
FRUITS=( APPLE BANANA ORANGE )
eval array=\( \${${ARRAYNAME}[@]} \)
for fruit in "${array[@]}"; do
echo ${fruit}
done
What you were originally trying to do was create an Indirect Reference. These were introduced in bash version 2 and were meant to largely replace the need for eval
when trying to achieve reflection-like behavior in the shell.
What you have to do when using indirect references with arrays is include the [@]
in your guess at the variable name:
#!/usr/bin/env bash
ARRAYNAME='FRUITS'
FRUITS=( APPLE BANANA ORANGE )
array="${ARRAYNAME}[@]"
for fruit in "${!array}"; do
echo $fruit
done
All that said, it's one thing to use Indirect References in this trivial example, but, as indicated in the link provided by Dennis Williamson, you should be hesitant to use them in real-world scripts. They are all but guaranteed to make your code more confusing than necessary. Usually you can get the functionality you need with an Associative Array.
Appending to an array w/ its name passed as a bash function parameter
Assuming bash 4.3 or newer, thus having namevars (declare -n
/ local -n
):
add_elem_to_array() {
local elem=$1 array_name=$2
local -n array=$array_name
array+=( "$elem" )
}
Supporting bash 3.x (particularly including 3.2, the oldest version in widespread use as of this writing):
add_elem_to_array() {
local elem=$1 array_name=$2
local cmd
printf -v cmd '%q+=( %q )' "$array_name" "$elem"
eval "$cmd"
}
That said -- given array+=( "$value" )
as an available syntax, there's little need for a function for the purpose, is there?
Bash: combine parameter indirection with arithmetic expansion?
If you create an array from the positional parameters, you can use array indexing to get the desired values.
args=( "$@" )
echo "${args[-1]}" # Last argument
echo "${args[-2]}" # Next to last argument
echo "${args[4]}" # Get the 5th argument (zero-based indexing)
echo ${args[SKIP+3]} # If SKIP=1, same as above.
Array indices are evaluated in an arithmetic context, the same as if you used $((...))
.
What is indirect expansion? What does ${!var*} mean?
If you read the bash
man page, it basically confirms what you have stated:
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.
However, reading on from there:
The exceptions to this are the expansions of
${!prefix*}
and${!name[@]}
described below.
${!prefix*}
Names matching prefix. Expands to the names of variables whose names begin with prefix, separated by the first character of theIFS
special variable.
In other words, your particular example ${!N*}
is an exception to the rule you quoted. It does, however, work as advertised in the expected cases, such as:
$ export xyzzy=plugh ; export plugh=cave
$ echo ${xyzzy} # normal, xyzzy to plugh
plugh
$ echo ${!xyzzy} # indirection, xyzzy to plugh to cave
cave
Related Topics
Are Pid-Files Still Flawed When Doing It 'Right'
Shutdown (Embedded) Linux from Kernel-Space
Grep Fails Inside Bash Script But Works on Command Line
How to Make Static Linked Elf File to Load Ld_Preload .So
Catching a Direct Redirect to /Dev/Tty
How Can Linux Ptrace Be Unsafe or Contain a Race Condition
Sed Permission Denied When Overwriting File
Linux Kernel API Changes/Additions
How to Reserve Virtual Memory in Linux
How to Retrieve Advertising Payload from Ibeacon/Ble
Best Text Search Engine for Integrating with Custom Web App
How to Fix Loading Plugins in Eclipse 3.5.1 on Linux
Haskell Ghc Compiling/Linking Error, Not Creating Executable. (Linux)
How to Create a File with Any Given Size in Linux