I'd like to create a Bash dictionary in a loop, then add it to an array:
#!/usr/bin/env bash
periods=()
for arg
do
echo "arg: $arg"
day= month= year=
case $arg in
*/*/* | *-*-*)
read -r year month day < <(date ' %Y %m %d' -d "$arg")
;;
????[-/]?? | ????[-/]?)
IFS='-/' read -r year month <<< "$arg"
;;
??[-/]???? | ?[-/]????)
IFS='-/' read -r month year <<< "$arg"
;;
esac
declare -A period
period[year]=$year
period[month]=$month
period[day]=$day
periods =(period)
done
# display
for period in "${!periods[@]}"
do
echo "year: ${period[year]} / month: ${period[month]} / day: ${period[day]}"
done
Unfortunately, it appears each period
instance in periods
is a reference to the last-added value:
$ ./parse.sh 3/14/19 3/14/2020
arg: 3/14/19
arg: 3/14/2020
year: 2020 / month: 03 / day: 14
year: 2020 / month: 03 / day: 14
Is there a way to define a Bash variable such that each instance is unique?
CodePudding user response:
bash
doesn't have data structure to support 2 dimensional arrays or a list of associative arrays as you are trying in this code, however you can use this simulation of 2 dimensional arrays in your bash code:
#!/usr/bin/env bash
declare -A periods
rec=0
for arg; do
echo "arg: $arg"
day= month= year=
case $arg in
*/*/* | *-*-*)
read -r year month day < <(date ' %Y %m %d' -d "$arg")
;;
????[-/]?? | ????[-/]?)
IFS='-/' read -r year month <<< "$arg"
;;
??[-/]???? | ?[-/]????)
IFS='-/' read -r month year <<< "$arg"
;;
esac
periods[$rec,year]="$year"
periods[$rec,month]="$month"
periods[$rec,day]="$day"
((rec ))
done
# check periods array content
declare -p periods
# display values from associative array
for ((i=0; i<rec; i )); do
echo "year: ${periods[$i,year]} / month: ${periods[$i,month]} / day: ${periods[$i,day]}"
done
Output:
arg: 3/14/19
arg: 3/14/2020
declare -A periods=([0,year]="2019" [1,day]="14" [0,month]="03" [0,day]="14" [1,year]="2020" [1,month]="03" )
year: 2019 / month: 03 / day: 14
year: 2020 / month: 03 / day: 14
CodePudding user response:
Depending on how hacky you wanna get, you can "trick" Bash into supporting arrays of associative arrays, though at this point I'd start to think of other languages I'd want to write this script in ;)
The gist of this is you're basically saving the structure of the associative array as a string in the array, keeping declare
s sandboxed from each other via functions, then eval
ing each assoc array back to a real bash data structure when you're ready to read from it.
This method is useful for "passing" associative arrays to functions, as well.
#!/usr/bin/env bash
periods=()
function main {
for arg
do
echo "arg: $arg"
day= month= year=
case $arg in
*/*/* | *-*-*)
read -r year month day < <(date ' %Y %m %d' -d "$arg")
;;
????[-/]?? | ????[-/]?)
IFS='-/' read -r year month <<< "$arg"
;;
??[-/]???? | ?[-/]????)
IFS='-/' read -r month year <<< "$arg"
;;
esac
declare -A period
period[year]="${year}"
period[month]="${month}"
period[day]="${day}"
save_to_array "period"
done
}
function save_to_array {
var=$(declare -p "$1")
periods =("${var}")
}
main "$@"
# display
for _per in "${periods[@]}" # note the lack of ! expansion
do
eval "declare -A final="${_per#*=} # new associative array `final` gets declared here
echo "year: ${final[year]} / month: ${final[month]} / day: ${final[day]}"
done