0

I have a command whose output is of the form:

[{"foo1":<some value>,"foo2":<some value>,"foo3":<some value>}]

I want to take the output of this command and just get the value corresponding to foo2 How do I use sed/awk or any other shell utility readily available in a bash script to do this?

1
  • Do you have a sample of different ":<some value>" content to define the possible delimiter Commented Dec 19, 2013 at 14:54

5 Answers 5

4

Assuming that the values do not contain commas, this sed rune will do it:

sed -n 's/.*"foo2":\([^,]*\),.*/\1/'p

sed -n tells sed not to print lines by default.

The s ("substitute") command uses a regexp group delimited by \( and \) to pick out just the bit you want.

"foo2": provides the context needed to find the right value.

[^,]* means "a character that is not a comma, any number of times". This is your . If values are not delimited by commas, change this (and the comma after the grouping parens) to match correctly.

.* means "any character, any number of times", and it is used to match all the characters before and after the bit you want. Now the regexp will match the entire line.

\1 means the contents of the grouping parentheses. sed will substitute the string that matches the pattern (which is the whole line, because we used .* at the beginning and end) with the contents of the parens, .

Finally, the p on the end means "print the resulting line".

Sign up to request clarification or add additional context in comments.

Comments

2

With this awk for example:

$ awk -F[:,] '{print $4}' file
<some value2>
  • -F[:,] sets possible field separators as : or ,. Then, it is a matter of counting the position in which <some value> of foo2 are. It happens to be the 4th.

With sed:

$ sed 's/.*"foo2":\([^,]*\).*/\1/g' file
<some value2>
  • .*"foo2":\([^,]*\).* gets the string coming after foo2: and until the comma appears. Then it prints it back with \1.

3 Comments

doesn not work if there is a ',' before ',"foo3"' so depend highly of content value assumption
Yes, for the awk version is true. As we do not have any more sample data, I assumed there is not. However, sed solution should solve it correctly.
agree as i assume also that content seem to be something taht does not contain , like a word or number. I ask for a sample of content to adapt the sed if needed
2

Your block of data looks like JSON. There is no native JSON parsing in bash, sed or awk, so ALL the answers here will either suggest that you use a different, more appropriate tool, or they will be hackish and might easily fail if your real data looks different from the example you've provided here.

That said, if you are confident that your variable:value blocks and line structure are always in the same format as this example, you may be able to get away with writing your own (very) basic parser that will work for just your use case.

Note that you can't really parse things in sed, it's just not designed for that. If your data always looks the same, a sed solution may be sufficient ... but remember that you are simply pattern matching, not parsing the input data. There are other answers already which cover this.

For very simple matching of the string that appears after the colon after "foo2", as Peter suggested, you could use the following:

$ data='[{"foo1":11,"foo2":222,"foo3":3333}]'
$ echo "$data" | sed -ne 's/.*"foo2":\([^,]*\),.*/\1/p'

As I say, this should in no way be confused with parsing of your JSON. It would work equally well (or badly) with an input string of abcde"foo2":bar,abcde.

In awk, you can make things that are a bit more advanced, but you still have serious limitations when it comes to JSON. For example, if you choose to separate fields with commas, but then you put a comma inside the <some value> in your data, awk doesn't know how to distinguish it from a field separator.

That said, if your JSON is only one level deep (i.e. matches your sample data), the following might work for you:

$ data='[{"foo1":11,"foo2":222,"foo3":3333}]'
$ echo "$data" | awk -F: -vRS=, '{gsub(/[^[:alnum:]]/,"",$1)} $1=="foo2" {print $2}'

This awk script considers commas as record separators and colons as field separators. It does not support any level of depth in your JSON, and depends on alphanumeric variable names. But it should handle JSON split on to multiple lines.

Alternately, if you want to avoid ugly hacks, and perl or python solutions don't work for you, you might want to try out jsawk. With it, you might use something like this:

$ data='[{"foo1":11,"foo2":222,"foo3":3333}]'
$ echo "$data" | jsawk -a 'return this.foo2'
[222]

SEE ALSO: Parsing json with awk/sed in bash to get key value pair

Comments

2

This worked for me. You can Try this one

echo "[{"foo1":<some value>,"foo2":<some value>,"foo3":<some value>}]" | awk -F"[:,]+" '{ if($3=="foo2") { print $4 }}'

Above line awk uses multiple field separators.I have used colon and comma here

Comments

1

Since this looks like JSON, let's parse it like JSON:

perl -MJSON -ne '$json = decode_json($_); print $json->[0]{foo2}, "\n"' <<END
[{"foo1":"some value","foo2":"some, value","foo3":"some value"}]
END
some, value

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.