Associative arrays are local by default
From: Greg Wooledge
Sent: Tue, 23 Aug 2011 06:53:27 -0700
Subject: Re: YAQAGV (Yet Another Question About Global Variables)bash 4.2 adds "declare -g" to create global variables from within a
function.
Thank you Greg! However Debian Squeeze still has Bash 4.1.5
How to use global arrays in bash?
The | while read element; do ... done
is running in a sub-shell, so its updates to the global are lost when the sub-shell exits.
One solution is to use bash's 'process substitution' to get the input to the while loop to run in a subshell instead. See:
Reading multiple lines in bash without spawning a new subshell?
declare global array in shell
There are several flaws to your script :
Your
if
tests should be written with[[
, not[
, which is for binary comparison (more info : here). If you want to keep[
or are not usingbash
, you will have to quote yourline
variable, i.e. write all your tests like this :if [ -f "$line" ];then
Don't use
ls
to list the current directory as it misbehaves in some cases. A glob would be more suited in your case (more info: here)If you want to avoid using a pipe, use a
for
loop instead. Replacels | while read line
withfor line in $(ls)
or, to take my previous point in acount,for line in *
After doing that, I tested your script and it worked perfectly fine. You should note that some folders will be listed under both under "executable files" and "directories", due to them having +x
rights (I don't know if this is the behaviour you wanted).
As a side note, you don't need to declare variables in bash before using them. Your first 6 lines are thus un-necessary. Variables i,j,k are not necessary as well as you can dynamicaly increment an array with the following syntax : normal+=("$line")
.
Bash - Array length (assignment) of Local Variable vs Global Variable
How do I correctly assign a local variable to receive array as input?
It doesn't matter whether the variables are local or global. In both cases, use parentheses like you would do in a manual array definition.
array=("${otherArray[@]}")
Supply arguments by command-line preferrably --- but I assume giving an array on command-line might be different
You can supply one array just fine
f() {
local x=("$@")
declare -p x
}
a=(1 2 3)
f "${a[@]}"
But for multiple arrays (as in f "${a[@]}" "${b[@]}"
) there is a problem. The function does not know where the first array ends and the second array starts. You could
- indicate the start/end by specifying the length of the first array
f() {
local xlen="$1";
shift
x=("${@:1:xlen}") y=("${@:xlen+1}")
declare -p x y
}
...
f "${#a[@]}" "${a[@]}" "${b[@]}"
- indicate the start/end by inserting a delimiter (
f "${a[@]}" , "${b[@]}"
) which must not appear in the arrays.
f() {
local dpos=$(printf %s\\0 "$@" | grep -zFxnm1 DELIM | sed 's/:.*//');
x=("${@:1:dpos-1}") y=("${@:dpos+1}")
declare -p x y
}
...
f "${a[@]}" DELIM "${b[@]}"
- do not pass the arrays, but only their names (
f a b
).
How to declare a global array within a function without the -g option?
As recommended by gniourf_gniourf, you can use printf -v
(in bash
4.1 or later)
printf -v "${fruit[i]}[x]" "%s" "$var"
or read
read "${fruit[i]}[x]" <<< "$var"
You probably just need to make sure the expression passed to eval
contains appropriate quotes.
$ fruit=(apple pear orange)
$ f () { eval "${fruit[$1]}[$2]=\"$3\""; }
$ f 0 2 'hi there' # apple[2]='hi there'
$ declare -p apple
declare -a apple='([2]="hi there")'
It should go without saying that this isn't recommended, given the risk for arbitrary code execution.
Let a function modify a global variable
You are passing, in effect, the names of each element of the array. Just pass the name of the array itself. Then use declare
to set the value of any particular element using indirect parameter expansion.
function my_function(){
elt2="$1[2]"
declare "$elt2=true"
}
my_function my_array
In bash
4.3, named references make this much simpler.
function my_function () {
declare -n arr=$1
arr[2]=true
}
my_function my_array
This only makes sense, though, if you intend to use my_function
with different global arrays. If my_function
is only intended to work with the global my_array
, then you aren't gaining anything by this: just work with the global as-is.
function my_function () {
my_array[2]=true
}
Bash script - global variable becomes empty when function exits
As i said in comments, you can use simple function calling, simple changes to your test code will be as follow:
#!/bin/bash
declare GLOBAL_VAR
function callThis(){
local output="==========================================\n"
output="$output [START callThis()]\n"
# For demonstration only - error 5 might be a built-in bash error code, but if caller
# expects 0/5 they can be handled
local statusCode=5
# This variable is empty as soon as it exits the function
GLOBAL_VAR="Some array value OR text output into GLOBAL_VAR"
# Because the script has alot of text (echo'd/printf) output, and the function
# calculates a result (array), I would like to have it as a RESULT
# for another function.
output="$output This is some output that will be sent to caller.\n"
output="$output Test to see if GLOBAL_VAR is assigned:\n"
output="$output '$GLOBAL_VAR'\n"
output="$output [END callThis()]\n"
output="$output ==========================================\n"
echo -e $output
return $((statusCode))
}
function startHere(){
# OUTPUT=$(callThis)
callThis
STATUS_RESULT=$?
# echo -e "[Text output : $OUTPUT\n]"
echo -e "[Status Code : $STATUS_RESULT\n]"
echo -e "[Global VAR in startHere(): '$GLOBAL_VAR']"
if [ "$STATUS_RESULT" -eq 5 ]; then
echo "Success! Status code $STATUS_RESULT found!"
fi
}
# echo -e $(startHere)
startHere
echo "Global VAR is now (outside functions): '$GLOBAL_VAR'"
output:
==========================================
[START callThis()]
This is some output that will be sent to caller.
Test to see if GLOBAL_VAR is assigned:
'Some array value OR text output into GLOBAL_VAR'
[END callThis()]
==========================================
[Status Code : 5
]
[Global VAR in startHere(): 'Some array value OR text output into GLOBAL_VAR']
Success! Status code 5 found!
Global VAR is now (outside functions): 'Some array value OR text output into GLOBAL_VAR'
also this can be helpful as an explanation for other methods for using sub-shell
Can't access global associate array using indirect expansion?
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}
Related Topics
Rename Part of File Name Based on Exact Match in Contents of Another File
How to Detect Usb Drive Insertion in Linux
Linux Task Schedule to Hour, Minute, Second
How to Ssh Multiple Hops Without Putting the Local Rsa Key Everywhere
Docker Networking Namespace Not Visible in Ip Netns List
How to Get the Nvidia Driver Version from the Command Line
What Is Start-Stop-Daemon in Linux Scripting
How to Force Linking with a Static Library When a Shared Library of Same Name Is Present
Printing an Integer with X86 32-Bit Linux Sys_Write (Nasm)
Using Ld_Preload Mixed 64Bit/32Bit Environment in Linux
How to Connect a Shell to a Pseudo Tty
Need an Overview of Debugging Process from the Hardware Layer
How to Configure a Systemd Service to Restart Periodically
How to Display Modified Date Time with 'Find' Command
How to Define a Bash Alias as a Sequence of Multiple Commands