Exporting associative arrays from one script to another - bash

I have a bash script which basically contains all export variables and I am trying to add associative array into that script.
This is my export's script:
#!/bin/bash
export declare -A oldLinks
oldLinks["A"]="linkA"
oldLinks["B"]="linkB"
oldLinks["C"]="linkC"
oldLinks["D"]="linkD"
export declare -A newLinks
newLinks["E"]="linkE"
newLinks["F"]="linkF"
newLinks["G"]="linkG"
newLinks["H"]="linkH"
This is the main script:
#!/bin/bash
source ArraysFile
for i in "${!oldLinks[#]}"
do
echo "${i} -> ${oldLinks[$i]}"
done
for i in "${!newLinks[#]}"
do
echo "${i} -> ${newLinks[$i]}"
done
This is the error which I'm getting:
export: `-A': not a valid identifier

Related

Saving a remote variable locally with heredoc through ssh

I am trying to run a local function remotely on a machine and retrieve the values of multiple variables of this function in a local function.
The first line of the heredoc section enables me to run the function remotely by defining it on the remote machine.
With the local machine named localmach and the remote machine named remotemach
#! /bin/bash
arr=()
scanHost()
{
arr+=("$(hostname)")
tmpResult=$(hostname)
}
scanHost
ssh user#remotemach "bash -s --" <<EOF
$(typeset -f scanHost)
tmpResult=$tmpResult
scanHost
EOF
echo "Local array -> ${arr[#]}"
echo "Local echo -> $tmpResult"
The snippet above returns
Local array -> localmach
Local echo -> localmach
But what I need is
Local array -> localmach remotemach
Local echo -> remotemach
In words, I need the value of the remote tmpResult AND the array arr stored locally.
Addendum :
Here I make the command hostname as an example, but in reality I am "scanning" a remote host and generate a JSON string that I store in the tmpResult variable. If I encounter problems during the scan, I append a string explaining the issue in arr. This way in my final json I can list all the problems encountered during the scan of multiple hosts.
You need to collect the result of ssh command :
#! /bin/bash
scanHost()
{
tmpResult=$(hostname)
}
scanHost
tmpResult=$(ssh user#remotemach "bash -s" <<EOF
$(typeset -f scanHost)
scanHost
echo "\$tmpResult"
EOF
)
echo "Local echo -> $tmpResult"
New Version
#! /bin/bash
arr=()
scanHost()
{
arr+=("$(hostname)")
tmpResult=$(hostname)
}
scanHost
eval "$(ssh user#remotemach "bash -s" <<EOF
$(typeset -f scanHost)
$(declare -p arr)
scanHost
declare -p arr
declare -p tmpResult
EOF
)"
echo "Local array -> ${arr[#]}"
echo "Local echo -> $tmpResult"
Use eval with caution due to side effects.

Pass specific variables from one shell script to another?

I have a bash script named test.sh with:
#!/bin/bash
var1=hello
sh test2.sh $var1="/var/log"
My test2.sh looks like this:
#!/bin/bash
function jumpto
{
label=$1
cmd=$(sed -n "/$label:/{:a;n;p;ba};" $0 | grep -v ':$')
eval "$cmd"
exit
}
start=${1:-"start"}
jumpto $start
start:
echo "variable -- " $var1
This does not work due to I used jumpto function. When execution, always $var1 assigned to the $1 variable in jumpto function.
Is there a different way to do this.?
We suggest to learn about environment variable and export command and their scope.
Try modify your test.sh
#!/bin/bash
export var1=hello
sh test2.sh $var1="/var/log"

Iterating array in declared function of bash shell script

I've been working through creating a script to move some files from a local machine to a remote server. As part of that process I have a function that can either be called directly or wrapped with 'declare -fp' and sent along to an ssh command. The code I have so far looks like this:
export REMOTE_HOST=myserver
export TMP=eyerep-files
doTest()
{
echo "Test moving files from $TMP with arg $1"
declare -A files=(["abc"]="123" ["xyz"]="789")
echo "Files: ${!files[#]}"
for key in "${!files[#]}"
do
echo "$key => ${files[$key]}"
done
}
moveTest()
{
echo "attempting move with wrapped function"
ssh -t "$REMOTE_HOST" "$(declare -fp doTest|envsubst); doTest ${1#Q}"
}
moveTest $2
If I run the script with something like
./myscript.sh test dev
I get the output
attempting move with wrapped function
Test moving files from eyerep-files with arg dev
Files: abc xyz
bash: line 7: => ${files[]}: bad substitution
It seems like the string expansion for the for loop is not working correctly. Is this expected behaviour? If so, is there an alternative way to loop through an array that would avoid this issue?
If you're confident that your remote account's default shell is bash, this might look like:
moveTest() {
ssh -t "$REMOTE_HOST" "$(declare -f doTest; declare -p $(compgen -e)); doTest ${1#Q}"
}
If you aren't, it might instead be:
moveTest() {
ssh -t "$REMOTE_HOST" 'exec bash -s' <<EOF
set -- ${##Q}
$(declare -f doTest; declare -p $(compgen -e))
doTest \"\$#\"
EOF
}
I managed to find an answer here: https://unix.stackexchange.com/questions/294378/replacing-only-specific-variables-with-envsubst/294400
Since I'm exporting the global variables, I can get a list of them using compgen and use that list with envsubst to specify which variables I want to replace. My finished function ended up looking like:
moveTest()
{
echo "attempting move with wrapped function"
ssh -t "$REMOTE_HOST" "$(declare -fp doTest|envsubst "$(compgen -e | awk '$0="${"$0"}"') '${1}'"); doTest ${1#Q}"
}

Shell script: Pass associative array to another shell script

I want to pass associate array as a single argument to shell script.
mainshellscript.sh :
#!/bin/bash
declare -A cmdOptions=( [branch]=testing [directory]=someDir [function1key]=function1value [function1key]=function1value )
./anothershellscript.sh cmdOptions
anothershellscript.sh :
#!/bin/bash
#--- first approach, didn't work, shows error "declare: : not found"
#opts=$(declare -p "$1")
#declare -A optsArr=${opts#*=}
#---
# second approach, didnt work, output - declare -A optsArr=([0]="" )
#declare -A newArr="${1#*=}"
#declare -p newArr
#---
I'm suspecting that they way I am collecting array in anothershellscript.sh is wrong, I'm finding a way to access map values simply by providing echo "${optsArr[branch]}" shall give testing.
I am using bash version 4.4.23(1)-release (x86_64-pc-msys).
Processes are separate - they do not share variables. A variable set in one process is not visible in the other process. Except: child processes inherit exported variables, but cmdOptions is not exported and you can't export associative arrays anyway.
So pass the string representation of the array to the child, either via arguments or with a file or with an exported variable:
#!/bin/bash
declare -A cmdOptions=( [branch]=testing [directory]=someDir [function1key]=function1value [function1key]=function1value )
export SOME_VAR="$(declare -p cmdOptions)" # env or
declare -p cmdOptions > some_file.txt # file or
./anothershellscript.sh "$(declare -p cmdOptions)" # arg
# Note - `declare -p` is called _on parent side_!
Then load in the child:
#!/bin/bash
declare -A newArr="${SOME_VAR#*=}" # env or
. some_file.txt # file or
declare -A newArr="${1#*=}" # arg
# How to load from file so that current environment is not affected:
tmp=$(
. some_file.txt
declare -p cmdOptions
)
declare -A newArr=${tmp#*=}"
Passing Associative array to sub script
Associative arrays and regular arrays are not well exported. But you could pass variables by using declare -p in some wrapper call.
First script:
#!/bin/bash
declare -A cmdOptions=( [branch]=testing [function1key]=function1value
[directory]=someDir [function2key]=function2value )
declare -a someArray=( foo "bar baz" )
declare -x someVariable="Foo bar baz" # Using *export* for simple variables
bash -c "$(declare -p cmdOptions someArray someVariable);. anothershellscript.sh"
Note syntax . anothershellscript.sh could be replaced by source anothershellscript.sh.
Doing so, prevent need of temporary files or temporary variable and keep STDIN/STDOUT free.
Then your anothershellscript.sh could use your variables as is.
#!/bin/bash
declare -p cmdOptions someArray someVariable
could work.
You'll have to rebuild that array in second script, here is an example script1:
#!/bin/bash
declare -A test=(
[v1]=1
[v2]=2
[v3]=3
)
for key in "${!test[#]}"; { data+=" $key=${test[$key]}"; }
echo "$data" | ./test2
script2:
#!/bin/bash
read -ra data
declare -A test2
for item in "${data[#]}"; {
key="${item%=*}"
val="${item#*=}"
test2[$key]="$val"
}
echo "${test2[#]}"

How to export an associative array (hash) in bash?

Related, but not a duplicate of: How to define hash tables in Bash?
I can define and use a bash hash, but I am unable to export it, even with the -x flag. For example, the following works to export (and test exportation of) a normal string variable:
aschirma#graphics9-lnx:/$ export animal_cow="moo"
aschirma#graphics9-lnx:/$ bash -c "echo \$animal_cow"
moo
aschirma#graphics9-lnx:/$
However, if I try to export a hash:
aschirma#graphics9-lnx:/$ declare -A -x animals
aschirma#graphics9-lnx:/$ animals[duck]="quack"
aschirma#graphics9-lnx:/$ echo ${animals[duck]}
quack
aschirma#graphics9-lnx:/$ bash -c "echo \${animals[duck]}"
aschirma#graphics9-lnx:/$
It seems the nested bash shell does not have the hash in its scope. I did verify this also by manually entering the nested bash shell and attempting to use the hash interactively.
There isn't really a good way to encode an array variable into the environment. See
http://www.mail-archive.com/bug-bash#gnu.org/msg01774.html (Chet Ramey is the maintainer of bash)
As a workaround for this harsh Bash limitation I'm using "serialize to temporary file" method. You can export plain variables, so you can pass an array (associative) through filepath. Of course, this has limitations, but sometimes works and is good enough.
declare -A MAP # export associative array
MAP[bar]="baz"
declare -x serialized_array=$(mktemp) # create temporary variable
# declare -p can be used to dump the definition
# of a variable as shell code ready to be interpreted
declare -p MAP > "${serialized_array}" # serialize an array in temporary file
# perform cleanup after finishing script
cleanup() {
rm "${serialized_array}"
}
trap cleanup EXIT
# ... in place where you need this variable ...
source "${serialized_array}" # deserialize an array
echo "map: ${MAP[#]}"
This is a bit old but I answer anyway, you could use temp files. If you do it right you can wrapper it to use them like arrays. For example with this function:
var() { # set var or add comtent
case $1 in
*=|*=*)
local __var_part1=$( echo "$1" | sed -e 's/=.*//' -e 's/[+,-]//' ) # cut +=/=
local __var_part2=$( echo "$1" | sed -e 's/.*.=//' )
local __var12=$tmp_dir/$__var_part1
mkdir -p ${__var12%/*} #create all subdirs if its an array
case $1 in
*+=*)
# if its an array try to add new item
if [ -d $tmp_dir/$__var_part1 ] ; then
printf -- $__var_part2 > $tmp_dir/$__var_part1/\ $((
$( echo $tmp_dir/$__var_part2/* \
| tail | basename )\ + 1 ))
else
printf -- "$__var_part2" >> $tmp_dir/$__var_part1
fi
;;
*-=*) false ;;
# else just add content
*) printf -- "$__var_part2" > $tmp_dir/$__var_part1 ;;
esac
;;
*) # just print var
if [ -d $tmp_dir/$1 ] ; then
ls $tmp_dir/$1
elif [ -e $tmp_dir/$1 ] ; then
cat $tmp_dir/$1
else
return 1
fi
;;
esac
}
# you can use mostly like you set vars in bash/shell
var test='Hello Welt!'
# if you need arrays set it like this:
var fruits/0='Apple'
var fruits/1='Banana'
# or if you need a dict:
var contacts/1/name="Max"
var contacts/1/surname="Musterman"
This not the fastest way, but its very flexible, simple and works in nearly all shells.
short answer --> export animals after declaring it
full -->
Try this way as a script:
#!/usr/bin/env bash
declare -A -x animals
export animals
animals[duck]="quack"
echo ${animals[duck]}
bash -c "echo ${animals[duck]}"
Output on my side using Bash version: 5.1.16(1)
quack
quack
or in terminal:
$ declare -A -x animals
$ export animals
$ animals[duck]="quack"
$ echo ${animals[duck]}
quack
$ bash -c "echo ${animals[duck]}"
quack
$

Resources