Home > Net >  How do I concatenate two strings with jq when one of them might be null?
How do I concatenate two strings with jq when one of them might be null?

Time:11-17

I'm trying to parse AWS instance metadata to take two values and combine them into one string (a name and an id). The name is sometimes empty, and jq doesn't want to append to null. How do I tell jq to just assume the null value is an empty string? I've seen mentions of a "destructuring" operator, or a map function to do this, but I can't seem to get any of that syntax to work.

It may further complicate things, but the name is stored as the value in a key-value tag. I have to do a select like this to get the name: .Tags[]|select(.Key == "Name").Value.

Here's some sample data:

{
  "InstanceId": "i-abc",
  "Tags": [
    {
      "Key": "Name",
      "Value": "Grafana"
    }
  ]
}
{
  "InstanceId": "i-def"
}

Here's what I'm trying:

cat sample.json |jq -r '.|{together: (.InstanceId   " "   (.Tags[]|select(.Key == "Name").Value) // empty)}'
{
  "together": "i-abc Grafana"
}
jq: error (at <stdin>:12): Cannot iterate over null (null)

CodePudding user response:

Don't overcomplicate things:

  • Get rid of the no-op .|
  • Use the error-suppression operator (?) when accessing the optional property.
  • The empty string is "", not empty. empty is a generator which generates nothing (with somewhat confusing effects).
  • Add parentheses to group expressions/define operator precedence.
{ together: (.InstanceId   " "   ((.Tags[]|select(.Key == "Name").Value)? // "")) }

Output:

{
  "together": "i-abc Grafana"
}
{
  "together": "i-def "
}

If you don't want to have the trailing blank, go with 0stone0's solution.

CodePudding user response:

Use parentheses to exclude .InstanceId but include " " … when failing:

{together: (.InstanceId   ((.Tags[] | " "   select(.Key == "Name").Value)? // ""))}
{
  "together": "i-abc Grafana"
}
{
  "together": "i-def"
}

Demo

CodePudding user response:

Another option would be to generate an array using select(), end then let join(" ") deal with the rest:

{ together: [ .InstanceId, (.Tags[]? | select(.Key=="Name").Value)  ] | join(" ") }
{
  "together": "i-abc Grafana"
}
{
  "together": "i-def"
}

JqPlay

CodePudding user response:

Before I got an answer, I asked a friend for help. He responded with another method:

cat sample.json|jq -r '((.Tags[]|select(.Key == "Name").Value)? // "") as $name|{together: (.InstanceId   " "   $name)}'
  • Related