Extract Key Value pairs which matches the regex in YAML with boolean values - yaml

I have this below YAML input and I am trying to extract shown output using yq. I want to remove pairs where key name (VAR-A) in value {{a.b.VAR-A}} (after a.b.) matches and If I have more than one {{a.b.VAR-A}} in values separated by - , I want to keep them.
VAR-A: '{{a.b.VAR-A}}'
VAR-B: '{{a.b.VAR-B}}'
VAR-C: v0.0
VAR-D: '{{a.b.VAR-D}}-{{a.b.VAR-A}}'
VAR-E: '{{a.b.VAR-C}}-{{a.b.VAR-B}}-{{a.b.VAR-A}}'
VAR-F: True
Expected Output:
VAR-C: v0.0
VAR-D: '{{a.b.VAR-D}}-{{a.b.VAR-A}}'
VAR-E: '{{a.b.VAR-C}}-{{a.b.VAR-B}}-{{a.b.VAR-A}}'
VAR-F: True
This question works if I have all strings, but it fails when I have boolean value in yaml. Extract Key Value pairs which matches the regex in YAML using yq/sed/grep
I get below error:
Error: cannot substitute with !!bool, can only substitute strings. Hint: Most often you'll want to use '|=' over '=' for this operation.

There are at least two very different extant "yq" projects: a Python-based one, which is the focus of Part 1 below, and a Go-based one, which is the focus of Part 2.
Part 1
python-yq 'del(.[] | select( ( type == "string" and test("^{{a[.]b[.][^}]*}}$" ))))' so-vars.yaml
or
python-yq 'map_values( select( ( type == "string" and test("^{{a[.]b[.][^}]*}}$" )) | not))' so-vars.yaml
Output:
{
"VAR-C": "v0.0",
"VAR-D": "{{a.b.VAR-D}}-{{a.b.VAR-A}}",
"VAR-E": "{{a.b.VAR-C}}-{{a.b.VAR-B}}-{{a.b.VAR-A}}",
"VAR-F": true
}
Part 2
The Go-based version of yq that I have (4.6.3) might not be able to handle your requirements directly, but here's a solution that uses this yq to translate to and from JSON, and jq to do the rest:
yq -j eval . input.yaml |
jq 'del(.[] | select(( type == "string" and test("^{{a[.]b[.][^}]*}}$" ))))' > tmp.json
yq -P eval . tmp.json
The del-free version of the jq program:
map_values( select( type == "string" and test("^{{a[.]b[.][^}]*}}$" | not)
Output:
VAR-C: v0.0
VAR-D: '{{a.b.VAR-D}}-{{a.b.VAR-A}}'
VAR-E: '{{a.b.VAR-C}}-{{a.b.VAR-B}}-{{a.b.VAR-A}}'
VAR-F: true

Related

jq How to pass key starting with numeral as argument [duplicate]

I am new to jq and facing an issue while parsing my json
I have a json stored in a variable like this
temp='{ "1": { "my_name": "one" }, "2": { "my_name": "two" } }'
Now I need to get the value of my_name for both other entries
I have tried something like this
echo $temp | jq '.1' //out put 0.1
I was assuming to get { "my_name": "one" }
And similarly to get my_name value I did
echo $temp | jq '.1.my_name' // Its output is giving me error
Can any one please help to identify what is wrong with my syntax and how can I correct it.
Just the number gets interpreted as a float. You need to use it in a context where it is unambiguously a key string.
echo "$temp" | jq '.["1"]["my_name"]'
and
echo "$temp" | jq '.["1"]'
to get the containing dict.
With a sufficiently new jq (I think >= 1.4) you can also say
echo "$temp" | jq '."1".my_name'
Whenever you are trying to reference a key that is not a valid identifier, you have to quote it. See the manual for more details.
To select the item under the key 1, you'd do this:
."1"
For your other question on how to obtain the my_name values, you could do this:
to_entries | map(.value.my_name)

How to use(read) value from associative array during jq map?

I am reading aws ssm parameters via jq with json, and want to put them into a yaml file after some processing and alias mapping.
I have one associative array of mapping parameter name to some internal name (see process.sh)
My simplified example.json is
{
"Parameters": [
{
"Name": "/dev/applications/web/some_great_key",
"Value": "magicvaluenotrelevant"
},
{
"Name": "/dev/applications/web/api_magic",
"Value": "blabla"
}
]
}
My bash script process.sh is
#!/usr/bin/env bash
declare -A ssmMap
ssmMap[/dev/applications/web/some_great_key]=theGreatKEYAlias
ssmMap[/dev/applications/web/api_magic]=apiKey
jq -r '
.Parameters
| map({
Name: .Name,
parameterValue: .Value
})
| map((${!ssmMap[.Name]} + ": \"" + .parameterValue + "\""))
| join("\n")
' < example.json;
I want/expected the output to be:
ssm_theGreatKEYAlias: "magicvaluenotrelevant"
ssm_apiKey: "singleblabla"
With the process.sh script that I provided I get error because cannot find a way to use the associative array sshMap inside jq map, to transform the parameter name from json into the mapped alias from sshMap.
Any idea how that can be achieved ?
If I change the line 13 of process.sh into
| map((.Name + ": \"" + .parameterValue + "\""))
it works fine but without mapping, uses original parameter name as comes from json file.
You're trying to access a shell var from jq.
Fixed:
ssm_map='
{
"some_great_key": "theGreatKEYAlias",
"api_magic": "apiKey"
}
'
jq -r --argjson ssm_map "$ssm_map" '
.Parameters[] |
"ssm_\( $ssm_map[ .Name ] ): \"\( .Value )\""
'
Demo on jqplay
You could convert the variable you describe into JSON with varying levels of difficulty (depending on what limitations, if any, you're willing to accept). But since you're building the variable from the output of yq, should could simply have it output JSON directly.
Using mikefarah/yq or kislyuk/yq or itchyny/gojq is definitely the better approach if your actual task is to convert from YAML and to process as JSON.
However, to accomplish the task you have described in the original question, one could add the keys and values of the bash associative array using the --args option and access them within jq via the $ARGS.positional array (requires jq version 1.6), then restore the association by transposeing both halves of that array, and create an INDEX using the key from the first half, which can then be used with JOIN to create the desired text lines, which are output as raw text using the -r option.
jq -r '
JOIN(
$ARGS.positional | [.[:length/2], .[length/2:]] | transpose | INDEX(.[0]);
.Parameters[];
.Name;
"ssm_\(.[1][1] // .[0].Name): \(.[0].Value)"
)
' example.json --args "${!ssmMap[#]}" "${ssmMap[#]}"
ssm_theGreatKEYAlias: magicvaluenotrelevant
ssm_apiKey: blabla
The fallback // .[0].Name is unnecessary for the sample file, but was added to use the original key in case there's an input key not covered by the bash array.

How do I concatenate dummy values in JQ based on field value, and then CSV-aggregate these concatenations?

In my bash script, when I run the following jq against my curl result:
curl -u someKey:someSecret someURL 2>/dev/null | jq -r '.schema' | jq -r -c '.fields'
I get back a JSON array as follows:
[{"name":"id","type":"int","doc":"Documentation for the id field."},{"name":"test_string","type":"string","doc":"Documentation for the test_string field"}]
My goal is to do a call with jq applied to return the following (given the example above):
{"id":1234567890,"test_string":"xxxxxxxxxx"}
NB: I am trying to automatically generate templated values that match the "schema" JSON shown above.
So just to clarify, that is:
all array objects (there could be more than 2 shown above) returned in a single comma-delimited row
doc fields are ignored
the values for "name" (including their surrounding double-quotes) are concatenated with either:
:1234567890 ...when the "type" for that object is "int"
":xxxxxxxxxx" ...when the "type" for that object is "string"
NB: these will be the only types we ever get for now
Can someone show me how I can expand upon my initial jq to return this?
NB: I tried working down the following path but am failing beyond this...
curl -u someKey:someSecret someURL 2>/dev/null | jq -r '.schema' | jq -r -c '.fields' | "\(.name):xxxxxxxxxxx"'
If it's not possible in pure JQ (my preference) I'm also happy for a solution that mixes in a bit of sed/awk magic :)
Cheers,
Stan
Given the JSON shown, you could add the following to your pipeline:
jq -c 'map({(.name): (if .type == "int" then 1234567890 else "xxxxxxxxxx" end)})|add'
With that JSON, the output would be:
{"id":1234567890,"test_string":"xxxxxxxxxx"}
However, it would be far better if you combined the three calls to jq into one.

Use sed with jq in bash script

How to use sed with jq to replace _ in key name with symbol a
{ "product_name":"kl" }
should become
{ "productaname":"kl" }
in a bash script
No need for sed; it's easy to do in just jq:
$ jq '{ productaname: .product_name }' <<<'{ "product_name":"kl" }'
{"productaname":"kl"}
If you want to replace underscores with a's in all keys of an object:
$ jq 'with_entries(.key |= gsub("_"; "a"))' <<<'{ "product_name":"kl", "foo_bar":12 }'
{"productaname":"kl","fooabar":12}
From the documentation for with_entries:
to_entries, from_entries, with_entries
These functions convert between an object and an array of key-value pairs. If to_entries is passed an object, then for each k: v entry in the input, the output array includes {"key": k, "value": v}.
from_entries does the opposite conversion, and with_entries(foo) is a shorthand for to_entries | map(foo) | from_entries, useful for doing some operation to all keys and values of an object. from_entries accepts key, Key, name, Name, value and Value as keys.

How can I convert a "key: value" sequence into JSON?

hokay, I am trying to write a script that takes information from the yum - repolist all and puts it into pretty JSON for me to use in some data collecting.. Right now I have my output from the yum command looking like this.
All I have for code right now is just the yum repolist command.
#!/bin/bash -x
yum -v repolist all | grep -B2 -A6 "enabled" | sed 's/[[:space:]]//g' , 's/--//g' , 's/name=name=/name=/g'
the output from that command looks like:
Repo-id: wazuh_repo
Repo-name: Wazuhrepository
Repo-status: enabled
Repo-revision: 1536348945
Repo-updated: FriSep712:35:512018
Repo-pkgs: 73
Repo-size: 920M
Repo-baseurl: https://packages.wazuh.com/3.x/yum/
Repo-expire: 21,600second(s)(last:WedOct3108:59:002018)
There are about 8 entries and the titles are always the same... Can someone explain like I am five how to convert this into json, I've read the jq man page, I've read about hash's. nothing seems to make sense. I know I need to have a "key"/"value" how to I designate these?
I just want to take the output and make it look like pretty JSON, this is part of a larger script I am writing to help keep ontop of the repos we use at work. I am just totally not getting JSON though.
edit: I would prefer not to use a wrapper function and do/learn the proper way
So, first, so people who don't have yum can test this, let's make a wrapper function:
write_output() { cat <<EOF
Repo-id: wazuh_repo
Repo-name: Wazuhrepository
Repo-status: enabled
Repo-revision: 1536348945
Repo-updated: FriSep712:35:512018
Repo-pkgs: 73
Repo-size: 920M
Repo-baseurl: https://packages.wazuh.com/3.x/yum/
Repo-expire: 21,600second(s)(last:WedOct3108:59:002018)
EOF
}
Notably, all your keys come before the string :, and the values come after them -- so we want to read line-by-line, split based on colon-space sequences, treat what was in front as a key, and treat what's in back as a value.
Given that:
jq -Rn '[inputs | split(": ")] | reduce .[] as $kv ({}; .[$kv[0]] = $kv[1])' < <(write_output)
...properly emits:
{
"Repo-id": "wazuh_repo",
"Repo-name": "Wazuhrepository",
"Repo-status": "enabled",
"Repo-revision": "1536348945",
"Repo-updated": "FriSep712:35:512018",
"Repo-pkgs": "73",
"Repo-size": "920M",
"Repo-baseurl": "https://packages.wazuh.com/3.x/yum/",
"Repo-expire": "21,600second(s)(last:WedOct3108:59:002018)"
}
...so, how does that work?
jq -R turns on raw input mode; input is parsed as a sequence of raw strings, not as a sequence of JSON documents.
jq -n treats null as the only direct input, so one can then use input and inputs primitives inside the script where needed.
[ inputs ] reads all your lines of input, and puts them into a single array.
[ inputs | split(": ")] changes that from an array of strings to an array of lists -- with content both before and after the ": " sequence.
reduce .[] as $kv ( {}; ... ) starts a reducer, with an initial value of {}, and then feeds each value that .[] evaluates to (which is to say, each item in your list) into that reducer (the ... code) as the $kv variable, replacing the . value each time.
To run this with your yum command as the real input, change < <(write_output) to < <(yum -v repolist all | grep -B2 -A6 "enabled" | sed 's/[[:space:]]//g' , 's/--//g' , 's/name=name=/name=/g').
Here is a slightly more robust variation of #CharlesDuffy's answer. Since the latter provides excellent explanatory notes, further explanations are not given here.
jq -nR '
[inputs | index(": ") as $ix | {(.[:$ix]): .[$ix+2:]}]
| add'
This avoids using split in case the "value" contains ": ". It might, however, be still better not to assume that a space follows the first relevant ":".
Notice also that add is used here instead of reduce, solely for compactness and simplicity.
For these sorts of problems, I would prefer to use a regular expression to match keys and values. Otherwise, I would take an approach similar to Charles's.
$ ... | jq -Rn 'reduce (inputs | capture("(?<k>[^:]+):\\s*(?<v>.+)")) as {$k, $v} ({}; .[$k] = $v)'

Resources