2

I am using Ubuntu and I want to extract the values between two patterns and the required string is not in the file.

My data is as shown below:

[{"rows":[{"_uuid":["uuid","11111-222-33333-4444444"]}]}]

I want to get the text between , and ], and that means I want 11111-222-33333-444444. How can I do this using sed?

  1. Ubuntu is the operating system I'm using.
  2. It was not stored in any file or variable. It was the output of one command. I want to pipe the output of command1 to sed and parse the above string to get only the required information.
  3. It was in JSON format. This was the only data we would be getting...
0
11

Using jq (which does not care whether the input is in compact or multi-line form):

your-command | jq -r '.[0].rows[0]._uuid[1]'

Your JSON document is an array consisting of objects and you want the first of these top-level objects, .[0]. That object contains a rows array, and you want the first of its elements, .rows[0]. That element has another array called _uuid, and you want that array's second element, ._uuid[1].

With -r you get the decoded, "raw", data back. Without -r, you get a (quoted) JSON string.


A totally different way of getting the data from this particular JSON document would be to get the last value in the document:

your-comand | jq -r 'getpath([paths(scalars)][-1])'

This first generates all "paths" for each scalar value in the whole document with paths, and picks out the last of them. The expression then uses this path-of-the-last-scalar with getpath to pull out the last value. For the given document, this results in the expected output.

The following is probably doing the same thing, but with explicit recursion using .. and with select() to pull out all scalar values:

your-command | jq -r '[.. | select(scalars)][-1]'

Personally, I would go with the topmost suggestion in this answer, as it uses the structure of the document, which is bound to be meaningful to the user in some way. That code would have to be revisited, and the question reformulated, if any of the involved arrays starts containing more elements.

0
3

If we can be certain that there is always only one , per line, and you want everything between that , and the first ] that comes after it, you can use sed:

 $ echo '[{"rows":[{"_uuid":["uuid","11111-222-33333-4444444"]}]}]' | sed 's/.*,\([^]]*\)\].*/\1/'
"11111-222-33333-4444444"

or, to also avoid the quotes:

   $ echo '[{"rows":[{"_uuid":["uuid","11111-222-33333-4444444"]}]}]' | sed 's/.*,"\([^]]*\)"\].*/\1/'
11111-222-33333-4444444

However, since this looks like JSON data, you should not use sed as this solution will break with even the smallest change in the input format. You should use a dedicated JSON parser like jq instead.

6
  • 1
    how can we use jq for the same problem statement Dec 6 '21 at 13:14
  • @KattaSivasai I don't know jq well enough to answer, sorry. As for the ' yes, you could remove it in this specific case, but don't: it is a good habit to always quote anything you pass to echo so that it is protected from the shell.
    – terdon
    Dec 6 '21 at 13:16
  • thanks @terdon for your valuable answers. Dec 6 '21 at 13:17
  • @KattaSivasai In some shells, depending on the shell's settings, an unquoted globbing pattern (which is what [...] would be interpreted as) would cause an error if the pattern does not match anything. This is the default in the zsh shell and would be the case in bash if the shell's failglob shell option was set. With nullglob set in bash, the string would disappear entirely if it does not match any filenames.
    – they
    Dec 6 '21 at 14:53
  • @they actually and intriguingly enough, with bash, echo [{...}] | jq (using the OP's string) fails with parse error: Invalid numeric literal at line 1, column 7 but echo '[{..}]' | jq works fine. Presumably because echo foo"bar"baz results in foobarbaz, so the double quotes are being removed.
    – terdon
    Dec 6 '21 at 14:57
1

If your valuable data contains just numbers and -, you can use this. (If uuid longer than 23, no problem.)

echo '[{"rows":[{"_uuid":["uuid","11111-222-33333-4444444"]}]}]' | sed  -n "s/.*,\"\([0-9-]\{23,\}\)\"].*/\1/p"

Output : 11111-222-33333-4444444

1
  • Thanks for all your quick responces, it's very helful Dec 6 '21 at 13:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.