I have a Bash script that I can't figure out how to quote a variable in.
Any help would be greatly appreciated.
This code works perfectly:
myfunction() {
for i in "${BASE_ARRAY[#]}"
do
I want to pass the name of my array as a variable to the function so I can reuse it with other arrays. This is the code I am trying that fails:
myfunction() {
for i in "${$1[#]}"
do
Then I pass the following to the function:
myfunction BASE_ARRAY
I've never had success passing arrays into functions.
For me, the two options are always to pass content into a function, or (since bash 4.3) pass in an array name which will be accessed using a reference. Consider the following example.
#!/usr/bin/env bash
myfunc() {
local -n arr=$1
printf '%s\n' "${arr[1]}"
arr[1]=HELLO
}
a=(one two three)
myfunc a
printf '%s\n' "${a[1]}"
which produces:
$ ./sample
two
HELLO
Note that local -n is like declare -n in that it doesn't provide a local copy of the array, but rather a local pointer to the original content. In this example, if you change $arr[], you are actually changing the original array, $a[].
The traditional method of passing array content to a function has been described so many times here on StackOverflow that it hardly bears mentioning; you'll have no difficulty finding examples.
Try this:
myfunction() {
local x="$1[#]"
for i in "${!x}"
do
Indirect references in Bash look like "${!VARIABLE_CONTAINING_NAME_TO_EXPAND}". It is straightforward for variables that are not arrays.
But when you need to access an item in an array (or all items like in your case), you need to put the whole reference in the variable to be expanded.
Related
First I've read about passing arrays in general -- all examples I saw first created temporary variable for array and then passed it. Taken from https://stackoverflow.com/a/26443029/210342
show_value () # array index
{
local -n myarray=$1
local idx=$2
echo "${myarray[$idx]}"
}
shadock=(ga bu zo meu)
show_value shadock 2
Is there a way to pass array directly as literal, i.e. without creating temporary variable?
I tried naive approach simply substituting the name with data, but I syntax error on "(".
Update:
I use openSUSE Leap 15.3 with bash 4.4. The above code of course works, but I would like to change the call into:
show_value (ga bu zo meu) 2
i.e. pass array data directly (without using extra variable).
If you want to change the order of the arguments:
show_value () # index array_element [...]
{
local idx=$1
local -a myarray=("${#:2}")
echo "${myarray[$idx]}"
}
then
shadock=(ga bu zo meu)
show_value 2 "${shadock[#]}" # => zo
If you want to keep the index as the last argument, then
show_value () # array_element [...] index
{
local -a myarray=("${#:1:$#-1}")
local idx=${!#}
echo "${myarray[$idx]}"
}
show_value "${shadock[#]}" 2 # => zo
local -n myarray=$1
is certainly much tidier than all that, isn't it? It will also be faster and more memory efficient -- you don't have to copy all the data.
Function arguments are not structured enough to handle nested data structures like arrays. Arguments are a flat list of strings, that's it.
You can expand the array inline:
show_value "${shadock[#]}" 2
However the delimiters are lost. There's no way to know where the array starts and end since it expands to:
show_value ga bu zo meu 2
You'll have to figure out the array bounds yourself. Options include:
If the command has only a single array parameter, make it the last one. This is what many traditional UNIX tools that take multiple file names do. Examples: ls <file>..., chmod <mode> <file>....
You can put the array in the middle if there's a fixed number of arguments preceding/following it such that you can unambiguously determine where the array is. Example: cp <file>... <dir>.
If you have multiple arrays, you can ask users to separate them with --. Many of git's more complicated subcommands do this.
I'd caution against taking an array name as an argument. That's a very shell-specific technique. It would make it hard to turn the function into a full-fledged script or binary down the road.
Can you consider this syntax :
show_value () # array index
{
local -a myarray=$1
local idx=$2
echo "${myarray[$idx]}"
}
show_value "(ga bu zo meu)" 2
For security reasons, you need to be sure of the contents of the array you pass into the function.
I know that there are arrays in bash-scripting. For example:
JOBS=("JOB1", "JOB2", "JOB3")
Then one can refer to, say, JOB2 as follows:
${JOBS[1]}
In a bash script I recently encountered a normal (non-array) variable:
JOB="XYZ"
Later in the script, this variable was referred to as follows:
${JOB[0]}
and:
${JOB[1]}
Since JOB is not an array, I do not understand what ${JOB[number]} is expanded to.
Is this just a programming mistake? Or is there some construct regarding normal variables I am not aware of?
bash doesn't have array values at all; it provides array syntax to use with names that have an array attribute set:
$ a=()
$ declare -p a
declare -a a=()
The -a in the output indicates that the array attribute is set on a, having been set implicitly by the preceding array assignment.
For such variables, the name-plus-index acts as a sort of "virtual" name, and like any other name, expands to the empty string if the name doesn't actually exist, as is the case with ${JOB[1]}. A side effect of the implementation is the $foo and ${foo[0]} are generally equivalent, whether or not foo has its array attribute set.
I believe newer versions of Bash support one-dimensional arrays and the syntax can be seen in the following link as follows :
Advanced Bash-Scripting Guide
If Variable is not an array, ${Variable[Number]} will work only for Number=0 but not for any other number as ${Variable[0]} and $Variable - both are same.
I am new to writing Shell Scripts and am having some difficulties.
What I Want To Achieve
I have an array of strings in scriptOne.sh that I want to pass to scriptTwo.sh
What I Have Done So Far
I can execute the second script from inside the first using ./scriptTwo.sh and I have passed string variables from one to the other using ./scriptTwo.sh $variableOne.
The issues are when I try to pass an array variable it doesn't get passed. I have managed to get it to pass the first entry of the array using ./scriptTwo.sh "${array[#]}" however this is only one of the entries and I need all of them.
Thanks in advance for your help
Your way of passing the array is correct
./scriptTwo.sh "${array[#]}"
The problem is probably in the way how you receive it. In scriptTwo.sh, use
array=("$#")
I think we can directly read the array content from sriptOne.sh if you use a declare command.
I declare an Associative array named wes_batch_array in scriptOne.sh and I want to pass it to scriptTwo.sh so I put the command below in scriptTwo.sh:
declare -A wes_batch_array=$(awk -F '=' '{if ($1 ~ /^declare -A wes_batch_array/) {for (i=2;i<NF;i++) {printf $i"=";} printf $NF"\n";}}' < scriptOne.sh)
I've tested this command and it works fine.
If you do not use the declare command. You can echo the content of array to another middle temp file and use the awk command above to grab the content of bash array.
The way I solved this problem was to step through the array in script1.sh, then pass the array elements to script2.sh using a for loop. In my case I was passing many arrays with varying numbers of elements to the same script2.sh on different servers.
So for example:
script1.sh:
records="111111111 222222222 333333333"
myRecordsArray=($records)
for (( i=0 ; i < $(#myRecordsArray[#]} ; i++ )); do
./script2.sh ${myRecordsArray[i]}
done
script2.sh:
main () {
<some code here>
}
myRecord=$1
main $myRecord
This is my simple array:
typeset -A foo
foo["first"]="first Value"
foo["second"]="second Value"
And I want to do a function that would pick this array, do something and return it back to the script. e.g.
function changeThat {
eval tmp=\$$1
tmp["$2"]=$3
return $tmp
}
I a way could go along in the script and do something like:
foo=changeThat foo "first" "a new first value"
And get a pretty result like
echo ${foo["first"]}
a new first value
Now this doesn't work... Well, I'm aware the syntax is prob not quite right. But I got really lost going through the nuances of evals and scape echo (not to say that I hate it from the bottom of my soul). Besides, my reference is for bash and wouldn't be the first time I miss some trick when it comes to ksh - For instance, I've been so far in ksh88, which does't even have associative arrays, while most people say it should. Turns out that my AIX box does not agree. -_-
thanks!
You can define your function like this:
function changeThat {
typeset -n ref="$1"
typeset key="$2"
typeset value="$3"
ref["$key"]="$value"
}
typeset -n ref defines the ref variable as a reference to the variable specified by it's value.
When you make this call to the function:
changeThat foo this "mow the lawn"
The variable ref in function changeThat references the variable foo. Using ref is now just like using foo. After calling changeThat
print ${foo["this"]}
will now output "mow the lawn".
I want to do a script of the sort:
#!/usr/bin/ksh93
typeset -A foo
function fillItUP {
typeset -A newarr
newarr["this"]="bar"
newarr["another"]="tut"
inputarrayname=$1
nameref $inputarrayname=newarr
}
With an output of the sort:
fillItUP "foo"
echo ${foo["this"]}
bar
I guess its pretty obvious, but what I thought of doing was using a double indirect reference to manipulate the array inside the function and later use it outside. Didn't work :(
Anyone would know a way to achieve this?
Thanks for the explanation . I understand what you are trying to pull off.
Now here is the working code
#!/usr/bin/ksh93
typeset -A foo
foo["this"]="old bar"
foo["another"]="old tut"
function fillItUP {
nameref newarr=$1
newarr["this"]="bar"
newarr["another"]="tut"
## nameref newarr=$1
}
fillItUP foo
echo ${foo["this"]}
The whole idea of nameref of typeset -n is to feed a variable from the one scope to the other. In your sample code, you were first allocating a local array to your function fillItUP (NOTE: why local ?? Read this on ...typeset and scope ) and then trying to point the local array to foo. If you want to change foo.. you need to make the local variable point to foo and then change it.
If you uncomment the 'commented nameref' and comment the 'uncommented nameref'
You will see that the value of foo is still "old bar".
If you execute the code I have added as it is, you will see that the value if foo[this]=bar and not the "old bar"
Hope this helped.
NOTE: You can comment out the initial "old" contents of foo and try it too :)