I had the following piece of az cli output in plain text:
echo $raw_containers_string
[
{
"name": "123"
},
{
"name": "vbm-container"
}
]
After some text refinement, I have a string returned containing this (zsh):
echo $raw_containers_string | grep name | cut -d ":" -f2 | tr '\n' " "
"123" "vbm-container" %
(It also has a % symbol at the end, but that's expected)
I now need to create an array of these 2 strings (123 and vbm-container) to iterate through it.
declare -a arr=($(echo $raw_containers_string | grep name | cut -d ":" -f2 | tr '\n' " "))
- returns
"123" "vb -co t i r"
- returns
arr=($(echo $raw_containers_string | grep name | cut -d ":" -f2 | tr '\n' " "))
- returns
"123" "vb -co t i r"
- returns
These are indices of an array (if it matters):
➜ bash-az-list-blobs git:(master) ✗ echo $myvar[0]
➜ bash-az-list-blobs git:(master) ✗ echo $myvar[1]
"123" "vb
➜ bash-az-list-blobs git:(master) ✗ echo $myvar[2]
-co
➜ bash-az-list-blobs git:(master) ✗ echo $myvar[3]
t
➜ bash-az-list-blobs git:(master) ✗ echo $myvar[4]
i
➜ bash-az-list-blobs git:(master) ✗ echo $myvar[5]
➜ bash-az-list-blobs git:(master) ✗ echo $myvar[6]
r"
Questions
- I want to understand why the behaviour between printing it to the terminal and assigning it to a variable is different.
- I would also
like to know how I assign my refined to an array in zsh, so that
echo arr
returns a 2-elements iterable array.
CodePudding user response:
You're counting on word-splitting of unquoted variable/command substitutions to separate the elements of the array, and have apparently also changed IFS
. These are both bad ideas, and together they're a recipe for chaos. You're also using zsh for interactive testing, and it does word splitting very differently from bash, so you'll get different weird results from it.
First, track down what's changing IFS
and fix it. You appear to have it set to something really weird, containing at least "m", "n", "a", and "e", so it's probably set by mistake.
If it actually does need to be changed, you need to keep it from staying changed. You can either make the change local to a specific command by making it a prefix of the command (e.g. IFS=',' read field1 field2
) or by resetting it after using the changed value (e.g. saveIFS="$IFS"; IFS=","; doSomethingWithIFS; IFS="$saveIFS"), or just find a different way to solve whatever you changed
IFS` for in the first place.
Second, get in the habit of double-quoting variable and command substitutions. There are some specific situations where you shouldn't double-quote them, and some situations where it's optional, but it's safest to just assume it should be double-quoted in most cases. shellcheck.net is good at pointing out quoting problems (and many other common mistakes).
Instead of using word-splitting on an unquoted substitution to populate the array, use readarray -t
to read each line of input into a separate array element (or read -ra
to read words into elements -- but fix IFS
first, or it'll also split weirdly). Note that you can't put these in a pipeline, or they execute in a subshell and the value gets lost; instead, use process substitution: readarray -t < <(somecommand)
Third, that grep | cut
thing is going to output something with extra spaces and quotes around the actual values. Anyway, when parsing JSON, it's better to use a tool like jq
that actually speaks the language.
So, with all that fixed, try this:
readarray -t arr < <(echo "$raw_containers_string" | jq -r '.[].name')
(And if you need to check the contents of an array -- or even a plain variable -- declare -p arr
is more reliable than echo
.)
CodePudding user response:
Using jq
:
eval "names=($(echo "$raw_containers_string" |
jq -r '.[].name|@sh'))"
Now you have a shell array names
, where each name is an element. Piping to @sh
means each value is quoted as a separate shell word, safe to eval
.