I am trying to make an array with Jq
My code is:
all='('$(cat players_temp.json | jq -r '.item1.items[1].firstName, .item1.items[1].lastName')')'
It gives the output
$ echo $all
(Luka Modrić)
$ echo $all[1]
(Luka Modrić)[1]
as you can see the array does not work like an array. I was expecting this:
$ echo $all[1]
Modrić
To create a bash array from jq output, see e.g. the following SO page:
How do I convert json array to bash array of strings with jq?
To understand why your approach failed, consider this transcript of a session with the bash shell:
$ all='('"Luka Modrić"')'
$ echo $all
(Luka Modrić)
$ echo $all[1]
(Luka Modrić)[1]
This essentailly shows that your question has nothing to do with jq at all.
If you want $all to be an array consisting of the two strings "Luka" and "Modrić" then you could write:
$ all=("Luca" "Modrić")
echo ${all[1]}
Modrić
$ echo ${all[0]}
Luca
Notice the correct bash syntax for arrays, and that the index origin is 0.
Summary
See the above-mentioned SO page for alternative ways to create a bash array from jq output.
The syntax for creating a bash array from a collection of strings can be summarized by:
ary=( v0 ... )
If ary is a bash array, ${ary[i]} is the i-th element, where i ranges from 0 to ${#ary[#]} - 1.
Related
I am trying to receive output from an aws-cli command into Bash, and use it as an input to the second.
I successfully saved the output into the variable with this helpful answer:
iid=$(aws ec2 run-instances ...)
and receive output like that:
{ "ImageId": "ami-0abcdef1234567890", "InstanceId": "i-1231231230abcdef0", "InstanceType": "t2.micro", ... }
I know that Bash since v4 supports associative arrays but I'm struggling converting one into one.
I tried to parse the dictionary in Bash but received error:
must use subscript when assigning associative array
This is because the right key syntax for Bash dicts is =, not :.
Finally I accessed the members by using this marvelous answer with sed:
echo $iid|sed 's/{//g;s/}//g;s/"//g'|cut -d ':' -f2
My question: is there any standard way of creating a Bash dictionary from JSON or text besides regex? Best-practice?
Considering the snippet from the answer I used, the sed-approach can be very verbose and the verbosity increases exponentially with the number of keys/members:
for items in `echo $db|sed 's/{//;s/}//'`
do
echo one${count} = `echo $items|sed 's/^.*\[//;s/\].*$//'|cut -d ',' -f1`
echo two${count} = `echo $items|sed 's/^.*\[//;s/\].*$//'|cut -d ',' -f2`
echo three${count} = `echo $items|sed 's/^.*\[//;s/\].*$//'|cut -d ',' -f3`
echo four${count} = `echo $items|sed 's/^.*\[//;s/\].*$//'|cut -d ',' -f4`
...
done
For simple dicts it is OK, but for the complex dictionaries with hundreds of keys and a big nesting level it is almost inapplicable.
Is there any unified approach for arbitrary dictionary?
P.S. I found answers about solving the opposite (receiving Bash dict in Python), solving the task through jq, creating dict from Bash to Bash and from non-common input but nothing about specifically JSON. I prefer not to use jq and python and stick to the standard Bash toolset, most of the answers of this collective answer use 3rd-party tools. Is it possible at all?
One way to turn your JSON object members into a Bash4+'s associative array:
#!/usr/bin/env bash
# shellcheck disable=SC2155 # Associative array declaration from JSON
declare -A assoc=$(
jq -r '"(",(to_entries | .[] | "["+(.key|#sh)+"]="+(.value|#sh)),")"' \
input.json
)
# Debug dump the Associative array declaration
typeset -p assoc
Sample output:
declare -A assoc=([InstanceId]="i-1231231230abcdef0" [InstanceType]="t2.micro" [ImageId]="ami-0abcdef1234567890" )
I have below file:
Site is facebook.
Site is microsoft.
Site is google.
And below script:
#!/bin/bash
#tried arr=$(awk {'print'} test) which gives array length as 1
arr=($(awk {'print'} test))
echo "Length ::: ${#arr[#]}"
Here the expected output is 3. However, I am getting length of array as 9. Above is just an excerpt from a script and need to use awk here.
Please let me know where the issue is....
This is the correct way to build a shell array of one entry per line from output of awk (requires bash 4+):
readarray -t arr < <(awk '1' file)
declare -p arr
declare -a arr=([0]="Site is facebook." [1]="Site is microsoft." [2]="Site is google")
When you use this code:
arr=($(awk '1' file))
Then shell splits on default delimiter and assigns each word from awk output to a separate array entry.
Having said that please bear in mind that awk is capable of doing everything that shell can do and it is always better to process your data in awk itself.
I am attempting to get a list of running VirtualBox VMs (the UUIDs) and put them into an array. The command below produces the output below:
$ VBoxManage list runningvms | awk -F '[{}]' '{print $(NF-1)}'
f93c17ca-ab1b-4ba2-95e5-a1b0c8d70d2a
46b285c3-cabd-4fbb-92fe-c7940e0c6a3f
83f4789a-b55b-4a50-a52f-dbd929bdfe12
4d1589ba-9153-489a-947a-df3cf4f81c69
I would like to take those UUIDs and put them into an array (possibly even an associative array for later use, but a simple array for now is sufficient)
If I do the following:
array1="( $(VBoxManage list runningvms | awk -F '[{}]' '{print $(NF-1)}') )"
The commands
array1_len=${#array1[#]}
echo $array1_len
Outputs "1" as in there's only 1 element. If I print out the elements:
echo ${array1[*]}
I get a single line of all the UUIDs
( f93c17ca-ab1b-4ba2-95e5-a1b0c8d70d2a 46b285c3-cabd-4fbb-92fe-c7940e0c6a3f 83f4789a-b55b-4a50-a52f-dbd929bdfe12 4d1589ba-9153-489a-947a-df3cf4f81c69 )
I did some research (Bash Guide/Arrays on how to tackle this and found this with command substitution and redirection, but it produces an empty array
while read -r -d '\0'; do
array2+=("$REPLY")
done < <(VBoxManage list runningvms | awk -F '[{}]' '{print $(NF-1)}')
I'm obviously missing something. I've looked at several simiar questions on this site such as:
Reading output of command into array in Bash
AWK output to bash Array
Creating an Array in Bash with Quoted Entries from Command Output
Unfortunately, none have helped. I would apprecaite any assistance in figuring out how to take the output and assign it to an array.
I am running this on macOS 10.11.6 (El Captain) and BASH version 3.2.57
Since you're on a Mac:
brew install bash
Then with this bash as your shell, pipe the output to:
readarray -t array1
Of the -t option, the man page says:
-t Remove a trailing delim (default newline) from each line read.
If the bash4 solution is admissible, then the advice given
e.g. by gniourf_gniourf at reading-output-of-command-into-array-in-bash
is still sound.
I have seen way too many duplicates of this, but none of the answer codes or tips ever helped me, so I'm left confused.
input=/foo/bar/*;
#Contains something along the lines of
#/foo/bar/file1 /foo/bar/file2 /foo/bar/file3
#And I simply need
#/foo/bar/file3 /foo/bar/file2 /foo/bar/file1
output=($(for l in ${input[#]}; do echo $l; done | sort));
#Doesn't work, returns only the last entry from input
output=$(sort -nr ${input});
#Works, returns everything correctly reversed, but outputs the file contents and not the pathnames;
output=($(sort -nr ${input}));
#Outputs only the last entry and also its contents and not the pathname;
I tried many more options, but I'm not gonna fill this whole page with them, you get the gist.
Duplicates: (None of them helpful to me)
How can I sort the string array in linux bash shell?
How to sort an array in BASH
custom sort bash array
Sorting bash arguments alphabetically
You're confused about what is an array in bash: this does not declare an array:
input=/foo/bar/*
$input is just the string "/foo/bar/*" -- the list of files does not get expanded until you do something like for i in ${input[#]} where the "array" expansion is unquoted.
You want this:
input=( /foo/bar/* )
mapfile -t output < <(printf "%s\n" "${input[#]}" | sort -nr)
I don't have time to explain it. I'll come back later.
You can use sort -r with printf, where input containg glob string to match your filenames:
sort -r <(printf "%s\n" $input)
This works:
input=`foo/bar/*`
output=`for l in $input ; do echo $l ; done | sort -r`
I have the following find command with the following output:
$ find -name '*.jpg'
./public_html/github/screencasts-gh-pages/reactiveDataVis/presentation/images/telescope.jpg
./public_html/github/screencasts-gh-pages/introToBackbone/presentation/images/telescope.jpg
./public_html/github/StarCraft-master/img/Maps/(6)Thin Ice.jpg
./public_html/github/StarCraft-master/img/Maps/Snapshot.jpg
./public_html/github/StarCraft-master/img/Maps/Map_Grass.jpg
./public_html/github/StarCraft-master/img/Maps/(8)TheHunters.jpg
./public_html/github/StarCraft-master/img/Maps/(2)Volcanis.jpg
./public_html/github/StarCraft-master/img/Maps/(3)Trench wars.jpg
./public_html/github/StarCraft-master/img/Maps/(8)BigGameHunters.jpg
./public_html/github/StarCraft-master/img/Maps/(8)Turbo.jpg
./public_html/github/StarCraft-master/img/Maps/(4)Blood Bath.jpg
./public_html/github/StarCraft-master/img/Maps/(2)Switchback.jpg
./public_html/github/StarCraft-master/img/Maps/Original/(6)Thin Ice.jpg
./public_html/github/StarCraft-master/img/Maps/Original/Map_Grass.jpg
./public_html/github/StarCraft-master/img/Maps/Original/(8)TheHunters.jpg
./public_html/github/StarCraft-master/img/Maps/Original/(2)Volcanis.jpg
./public_html/github/StarCraft-master/img/Maps/Original/(3)Trench wars.jpg
./public_html/github/StarCraft-master/img/Maps/Original/(8)BigGameHunters.jpg
./public_html/github/StarCraft-master/img/Maps/Original/(8)Turbo.jpg
./public_html/github/StarCraft-master/img/Maps/Original/(4)Blood Bath.jpg
./public_html/github/StarCraft-master/img/Maps/Original/(2)Switchback.jpg
./public_html/github/StarCraft-master/img/Maps/Original/(4)Orbital Relay.jpg
./public_html/github/StarCraft-master/img/Maps/(4)Orbital Relay.jpg
./public_html/github/StarCraft-master/img/Bg/GameLose.jpg
./public_html/github/StarCraft-master/img/Bg/GameWin.jpg
./public_html/github/StarCraft-master/img/Bg/GameStart.jpg
./public_html/github/StarCraft-master/img/Bg/GamePlay.jpg
./public_html/github/StarCraft-master/img/Demo/Demo.jpg
./public_html/github/flot/examples/image/hs-2004-27-a-large-web.jpg
./public_html/github/minicourse-ajax-project/other/GameLose.jpg
How do I store this output in an array? I want it to handle filenames with spaces
I have tried this arrayname=($(find -name '*.jpg')) but this just stores the first element. # I am doing the following which seems to be just the first element?
$ arrayname=($(find -name '*.jpg'))
$ echo "$arrayname"
./public_html/github/screencasts-gh-pages/reactiveDataVis/presentation/images/telescope.jpg
$
I have tried here but again this just stores the 1st element
Other similar Qs
How do I capture the output from the ls or find command to store all file names in an array?
How do i store the output of a bash command in a variable?
If you know with certainty that your filenames will not contain newlines, then
mapfile -t arrayname < <(find ...)
If you want to be able to handle any file
arrayname=()
while IFS= read -d '' -r filename; do
arrayname+=("$filename")
done < <(find ... -print0)
echo "$arrayname" will only show the first element of the array. It is equivalent to echo "${arrayname[0]}". To dump an array:
printf "%s\n" "${arrayname[#]}"
# ............^^^^^^^^^^^^^^^^^ must use exactly this form, with the quotes.
arrayname=($(find ...)) is still wrong. It will store the file ./file with spaces.txt as 3 separate elements in the array.
If you have a sufficiently recent version of bash, you can save yourself a lot of trouble by just using a ** glob.
shopt -s globstar
files=(**/*.jpg)
The first line enables the feature. Once enabled, ** in a glob pattern will match any number (including 0) of directories in the path.
Using the glob in the array definition makes sure that whitespace is handled correctly.
To view an array in a form which could be used to define the array, use the -p (print) option to the declare builtin:
declare -p files