1

How to sed/awk for a string contained in another string.

TESTVAR='
{
 icon : "icons/773_l.png",
 id : 80,
 initialState : true,
 isPng : false,
 label : "Imagery",
 opacity : 1,
 requestType : "UUID",
 version : 79
}
,
{
 icon : "thisicon",
 id : 8080,
 initialState : false,
 isPng : true,
 label : "Boundaries",
 opacity : 1,
 requestType : "NothingSpecial",
 version : 8
}

I need select the version : 79 for the object that contains requestType : "UUID". How would I select everything inside the first block, then select the version, and just have a command return 79. I have been trying for some time but don't know how to only select everything between the first {}. Once I have that I would then just need to get the number after version :.

I thought this would work but it hasn't awk '/\{/{f=1;next}/\}/{f=0}f' test.txt

In addition, order is not guaranteed. So I really need to select everything inside { and } containing "UUID". Then select version : \([0-9]+\)

This is close but too greedy: sed -e 's/{\(.*UUID.*\)}/\1/' test.txt

1

First what comes to my mind is neither sed nor awk:

$ tr -d '\n' < file | grep -Po 'requestType : "UUID"\K.*? version : \K[0-9]*'
79

Probably can be done easier, especially if you can guarantee that "version" comes right after "requestType".

In case if version doesn't have to go after requested Type things are little bit more complicated:

$ tr -d '\n' < file | grep -Po '{.[^}]*?requestType : "UUID".*?}' | grep -Po 'version : \K[0-9]*'
79

If there are more sections which can match all of them will be printed:

$ cat file
TESTVAR='
{
 icon : "icons/773_l.png",
 id : 80,
 initialState : true,
 isPng : false,
 label : "Imagery",
 opacity : 1,
 version : 79,
 requestType : "UUID"
}
,
{
 icon : "thisicon",
 id : 8080,
 initialState : false,
 isPng : true,
 label : "Boundaries",
 opacity : 1,
 requestType : "NothingSpecial",
 version : 8
}
,
{
 icon : "icons/773_l.png",
 id : 80,
 initialState : true,
 isPng : false,
 label : "Imagery",
 opacity : 1,
 requestType : "UUID",
 version : 87
}
,
{
 icon : "icons/773_l.png",
 id : 80,
 version : 17,
 initialState : true,
 isPng : false,
 label : "Imagery",
 opacity : 1,
 requestType : "UUID"
}
,
{
 icon : "thisicon",
 id : 8080,
 requestType : "NothingSpecial",
 initialState : false,
 label : "Boundaries",
 opacity : 1,
 version : 18,
 isPng : true
}

$ tr -d '\n' < file | grep -Po '{.[^}]*?requestType : "UUID".*?}' | grep -Po 'version : \K[0-9]*'
79
87
17
  • Close. This works great for the example. Can't guarantee the "version" will come right after "requestType". I actually can't guarantee that it will always come AFTER. So if version comes first that will not work. – kosmos Sep 25 '14 at 17:46
  • See updated answer – jimmij Sep 25 '14 at 19:06
  • This is the one that works best for my case. Thanks – kosmos Sep 25 '14 at 19:30
1

awk lets you define the record separator, so instead of a newline (each line is a record), use "}\n" as the end of the record:

echo "$TESTVAR" |
gawk -v RS="}\n" '
    /requestType : "UUID"/ && match($0, /version : ([0-9]+)/, m) {print m[1]}
'
79

This is GNU awk specific, for the match() function.


Too bad this isn't valid JSON. Then you could use a JSON parser:

jq '(.[] | select(.requestType == "UUID")).version' <<JSON
[
 {
  "icon" : "icons/773_l.png",
  "id" : 80,
  "initialState" : true,
  "isPng" : false,
  "label" : "Imagery",
  "opacity" : 1,
  "requestType" : "UUID",
  "version" : 79
 }
 ,
 {
  "icon" : "thisicon",
  "id" : 8080,
  "initialState" : false,
  "isPng" : true,
  "label" : "Boundaries",
  "opacity" : 1,
  "requestType" : "NothingSpecial",
  "version" : 8
 }
]
JSON
  • I wish it was just JSON. It's a wget response with some javascript inside. Also will not have access to any libraries like jq. – kosmos Sep 25 '14 at 19:32
  • 1
    @kosmos See my edited answer for parsing this directly with a javascript interpreter – Digital Trauma Sep 25 '14 at 19:43
1

Here is one way to do this purely with sed:

$ sed -rn '/\{/{:a;N;/\}/{/requestType : "UUID"/s/.*version : ([0-9]+).*/\1/p;d};ba}' <<< "$TESTVAR"
79
$ 

This is adapted from https://stackoverflow.com/a/18046021/2113226 for your data.


You mention that this is a javascript data structure, so I think the most reliable method to parse it would be using javascript. I installed Node.js to do this, but I assume any command-line javascript interpreter should be able to do something like this:

$ echo "arr=[$TESTVAR]; console.log(arr.filter(function(elem) { return elem.requestType === \"UUID\"; })[0].version)" | node
79
$ 

Disclaimer

You'd have to be sure of the data here. I don't know javascript that well, but I think code-injection would be quite possible if the input data string was crafted just right.

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.