I have a requirement to parse the output of the npm ls --global --json command, such that I get a list of all the installed npm packages in the following format:
$package;$version;js;$resolved
Where:
$package is the key containing the package name, from each dependencies object.
$version is the version value taken from each package
js is just a literal string
$resolved is the resolved value taken from each package
I have gotten as far as this command syntax and output:
$ jq --raw-output 'select( has("dependencies") ) .dependencies[] | . as $d | "parentkey" + ";" + $d.version + ";js;" + $d.resolved'`
parentkey;5.5.1;js;
parentkey;1.1.3;js;https://registry.npmjs.org/yaml-table/-/yaml-table-1.1.3.tgz
The parts that I am specificly having difficulty with are as follows:
How can I get the key name value that I am iterating over in .dependencies that contains that package name. It seems that by that point I am looking at the contents of that object itself.
How can I recurse through ALL dependency objects? At the moment I'm only looking at the top level records in the root .dependencies object. I've discovered .. recursion, but I'm not quite sure how to apply it here.
Based on the example data below, I am trying to reach the following output results:
npm;5.5.1;js;
JSONStream;1.3.1;js;https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz
jsonparse;1.3.1;js;https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz
through;2.3.8;js;https://registry.npmjs.org/through/-/through-2.3.8.tgz
yaml-table;1.1.3;js;https://registry.npmjs.org/yaml-table/-/yaml-table-1.1.3.tgz
js-yaml;3.4.6;js;https://registry.npmjs.org/js-yaml/-/js-yaml-3.4.6.tgz
argparse;1.0.9;js;https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz
Some (much reduced) sample output npm ls --global --json that I have used for the above example, is as follows:
{
"dependencies": {
"npm": {
"version": "5.5.1",
"dependencies": {
"JSONStream": {
"version": "1.3.1",
"from": "JSONStream#~1.3.1",
"resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz",
"dependencies": {
"jsonparse": {
"version": "1.3.1",
"from": "jsonparse#^1.2.0",
"resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz"
},
"through": {
"version": "2.3.8",
"from": "through#>=2.2.7 <3",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz"
}
}
}
}
},
"yaml-table": {
"version": "1.1.3",
"from": "yaml-table#latest",
"resolved": "https://registry.npmjs.org/yaml-table/-/yaml-table-1.1.3.tgz",
"dependencies": {
"js-yaml": {
"version": "3.4.6",
"from": "js-yaml#3.4.6",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.4.6.tgz",
"dependencies": {
"argparse": {
"version": "1.0.9",
"from": "argparse#>=1.0.2 <2.0.0",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz"
}
}
}
}
}
}
}
By using .., it will recurse through all values in the json tree. So you'll want to filter those out by objects that have the structure you're expecting. In this case, things that have a valid dependencies object. Once you've located the objects, you could extract the values you want.
jq -r '.. | .dependencies? | objects
| to_entries[] | [.key, .value.version, "js", .value.resolved] | join(";")' input.json
produces the results:
npm;5.5.1;js;
yaml-table;1.1.3;js;https://registry.npmjs.org/yaml-table/-/yaml-table-1.1.3.tgz
JSONStream;1.3.1;js;https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz
jsonparse;1.3.1;js;https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz
through;2.3.8;js;https://registry.npmjs.org/through/-/through-2.3.8.tgz
js-yaml;3.4.6;js;https://registry.npmjs.org/js-yaml/-/js-yaml-3.4.6.tgz
argparse;1.0.9;js;https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz
Related
I think I'm a step off from figuring out how to jq reduce via filter a key to another objects sub-key.
I'm trying to combine files (simplified from Elasticsearch's ILM Explain & ILM Policy API responses):
$ echo '{".siem-signals-default": {"modified_date": "siem", "version": 1 }, "kibana-event-log-policy": {"modified_date": "kibana", "version": 1 } }' > ip1.json
$ echo '{"indices": {".siem-signals-default-000001": {"action": "complete", "index": ".siem-signals-default-000001", "policy" : ".siem-signals-default"} } }' > ie1.json
Such that the resulting JSON is:
{
".siem-signals-default-000001": {
"modified_date": "siem",
"version": 1
"action": "complete",
"index": ".siem-signals-default-000001",
"policy": ".siem-signals-default"
}
}
Where ie1 is base JSON and for a child-object, its sub-element policy should line up to ip1's key and copy its sub-elements into itself. I've been trying to build off this, this, and this (from StackOverflow, also this, this, this from external sources). I'll list various rabbit hole attempts building off these, but they're all insufficient:
$ ((cat ie1.json | jq '.indices') && cat ip1.json) | jq -s 'map(to_entries)|flatten|from_entries' | jq '. as $v| reduce keys[] as $k({}; if true then .[$k] += $v[$k] else . end)'
{
".siem-signals-default": {
"modified_date": "siem",
"version": 1
},
".siem-signals-default-000001": {
"action": "complete",
"index": ".siem-signals-default-000001",
"policy": ".siem-signals-default"
},
"kibana-event-log-policy": {
"modified_date": "kibana",
"version": 1
}
}
$ jq --slurpfile ip1 ip1.json '.indices as $ie1|$ie1+{ilm: $ip1 }' ie1.json
{
".siem-signals-default-000001": {
"action": "complete",
"index": ".siem-signals-default-000001",
"policy": ".siem-signals-default"
},
"ilm": [
{
".siem-signals-default": {
"modified_date": "siem",
"version": 1
},
"kibana-event-log-policy": {
"modified_date": "kibana",
"version": 1
}
}
]
}
I also expected something like this to work, but it compile errors
$ jq -s ip1 ip1.json '. as $ie1|$ie1 + {ilm:(keys[] as $k; $ip1 | select(.policy == $ie1[$k]) | $ie1[$k] )}' ie1.json
jq: error: ip1/0 is not defined at <top-level>, line 1:
ip1
jq: 1 compile error
From this you can see, I've determined various ways to join the separate files, but though I have code I thought would play into filtering, it's not correct / taking effect. Does anyone have an idea how to get the filter part working? TIA
This assumes you are trying to combine the .indices object stored in ie1.json with an object within the object stored in ip1.json. As the keys upon to match are different, I further assumed that you want to match the field name from the .indices object, reduced by cutting off everything that comes after the last dash -, to the same key in the object from ip1.json.
To this end, ip1.json is read in from input as $ip (alternatively you can use jq --argfile ip ip1.json for that), then the .indices object is taken from the first input ie1.json and to the inner object accessed via with_entries(.value …) is added the result of a lookup within $ip at the matching and accordingly reduced .key.
jq '
input as $ip | .indices | with_entries(.value += $ip[.key | sub("-[^-]*$";"")])
' ie1.json ip1.json
{
".siem-signals-default-000001": {
"action": "complete",
"index": ".siem-signals-default-000001",
"policy": ".siem-signals-default",
"modified_date": "siem",
"version": 1
}
}
Demo
If instead of the .indices object's inner field nane you want to have the content of field .index as reference (which in your sample data has the same value), you can go with map_values instead of with_entries as you don't need the field's name anymore.
jq '
input as $ip | .indices | map_values(. += $ip[.index | sub("-[^-]*$";"")])
'ie1.json ip1.json
Demo
Note: I used sub with a regex to manipulate the key name, which you can easily adjust to your liking if in reality it is more complicated. If, however, the pattern is infact as simple as cutting off after the last dash, then using .[:rindex("-")] instead will also get the job done.
I also received offline feedback of a simple "workable for my use case" but not exact answer:
$ jq '.indices | map(. * input[.policy])' ie1.json ip1.json
[
{
"action": "complete",
"index": ".siem-signals-default-000001",
"policy": ".siem-signals-default",
"modified_date": "siem",
"version": 1
}
]
Posting in case someone runs into similar, but other answer's better.
I have below json output from ansible, I want to parse this file using jq. I want to match packages name GeoIP and print version number.
How can I use jq to get this version number for matching package name? looking output like this
"GeoIP","1.5.0"
sample ansible output
{
"ansible_facts.packages": {
"GConf2": [
{
"arch": "x86_64",
"epoch": null,
"name": "GConf2",
"release": "8.el7",
"source": "rpm",
"version": "3.2.6"
}
],
"GeoIP": [
{
"arch": "x86_64",
"epoch": null,
"name": "GeoIP",
"release": "14.el7",
"source": "rpm",
"version": "1.5.0"
}
],
"ImageMagick": [
{
"arch": "x86_64",
"epoch": null,
"name": "ImageMagick",
"release": "18.el7",
"source": "rpm",
"version": "6.7.8.9"
}
],
}
}
First, make the source valid json by removing the comma in the third to last row.
With that done, we can start at the end and work back. The desired output can be produced using jq's #csv format. That, in turn, requires that the output is in an array. (See the section of the manual titled "Format strings and escaping".) So we need to get the data to look like this.
jq -r '["GeoIP","1.5.0"] | #csv'
One way to do that is to put each item in its own array and add the arrays together. (See the section titled "Addition".)
jq -r '["GeoIP"] + [.[] | .GeoIP[].version] | #csv'
Since map(x) is defined as [.[] | x], you can say this instead.
jq -r '["GeoIP"] + map(.GeoIP[].version) | #csv'
You can use a variable to specify the package name you want like this.
jq -r --arg "package" "GeoIP" '[$package] + map(.[$package][].version) | #csv'
Update
My original solution has unnecessary steps. The array can be made like this.
jq -r '[ "GeoIP", .[].GeoIP[].version ] | #csv'
Or, using a variable
jq -r --arg "package" "GeoIP" '[$package,(.[] | .[$package][].version)]| #csv'
Q: "How can I use jq to get the version number for matching package name?"
A: Try the script below
$ cat print-version.sh
#!/bin/sh
cat output.json | jq ".ansible_facts.packages.$1 | .[].version"
For example
$ print-version.sh GeoIP
"1.5.0"
Improved version of the script
Quoting the comment: "Using the shell's string interpolation as done here is risky and generally regarded as an "anti-pattern". – peak"
The improved script below does the job
$ cat print-version.sh
#!/bin/sh
cat output.json | jq ".ansible_facts.packages.$1[].version"
output.json
{
"ansible_facts": {
"packages": {
"GConf2": [
{
"arch": "x86_64",
"epoch": null,
"name": "GConf2",
"release": "8.el7",
"source": "rpm",
"version": "3.2.6"
}
],
"GeoIP": [
{
"arch": "x86_64",
"epoch": null,
"name": "GeoIP",
"release": "14.el7",
"source": "rpm",
"version": "1.5.0"
}
],
"ImageMagick": [
{
"arch": "x86_64",
"epoch": null,
"name": "ImageMagick",
"release": "18.el7",
"source": "rpm",
"version": "6.7.8.9"
}
]
}
}
}
I assume you want to get a loop over all key-values attributes in the ansible_facts.packages, so it's recommended to use a Bash loop to iterate over it. Also the sample contents of the JSON you provided don't compile, I removed the comma 3 rows from the end.
You need one jq to get all the keys of the ansible_facts.packages and then in each iteration to get the version of the given key (package).
To get all the keys:
[http_offline#greenhat-30 tmp]$ cat file.json | jq '."ansible_facts.packages" | keys[]'
"GConf2"
"GeoIP"
"ImageMagick"
[http_offline#greenhat-30 tmp]$
Loop:
for package in `cat file.json | jq '."ansible_facts.packages" | keys[]'`
do
VERSION=`cat file.json | jq ".\"ansible_facts.packages\".$package[0].version"`
echo "$package,$VERSION"
done
Sample run:
[http_offline#greenhat-30 tmp]$ for package in `cat file.json | jq '."ansible_facts.packages" | keys[]'`
> do
> VERSION=`cat file.json | jq ".\"ansible_facts.packages\".$package[0].version"`
> echo "$package,$VERSION"
> done
"GConf2","3.2.6"
"GeoIP","1.5.0"
"ImageMagick","6.7.8.9"
[http_offline#greenhat-30 tmp]$
Using jq, how can I take a json object from a file (input_02.json), and append it to output.json, while retaining everything already in output.json (e.g. an object originating from file input_01.json).
The object to be appended in both cases is literally the entire contents of the file, with the file's "id" field as the object's key.
I'm taking a large list of input files (all with the same syntax) and essentially combining them like that.
The command i'm using to create the object to be appended is as follows:
jq '{(.id):(.)} ' input_01.json
which gives me:
{
"input1_id": {
}
}
input_1.json:
{
"id": "input1_id",
"val": "testVal1"
}
input2.json:
{
"id": "input2_id",
"val": "testVal2"
}
desired output:
{
"input1_id": {
"id": "input1_id",
"val": "testVal1"
},
"input2_id": {
"id": "input2_id",
"val": "testVal2"
}
}
You’re on the right track with {(.id):(.)}. The following should handle the case you mentioned, and might give you some ideas about similar cases:
program.jq: map({(.id):(.)}) | add
Invocation:
jq -s -f program.jq input_01.json input_02.json
You could use "jf" for this from https://pypi.python.org/pypi/jf
$ pip install jf
$ jf 'chain(), {y["id"]: y for y in x}' input1.json input2.json
{
"input2_id": {
"id": "input2_id",
"val": "testVal2"
},
"input1_id": {
"id": "input1_id",
"val": "testVal1"
}
}
Using jq I tried to update this json document:
{
"git_defaults": {
"branch": "master",
"email": "jenkins#host",
"user": "Jenkins"
},
"git_namespaces": [
{
"name": "NamespaceX",
"modules": [
"moduleA",
"moduleB",
"moduleC",
"moduleD"
]
},
{
"name": "NamespaceY",
"modules": [
"moduleE"
]
}
]
}
with adding moduleF to NamespaceY. I need to write the file back again to the original source file.
I came close (but no cigar) with:
jq '. | .git_namespaces[] | select(.name=="namespaceY").modules |= (.+ ["moduleF"])' config.json
and
jq '. | select(.git_namespaces[].name=="namespaceY").modules |= (.+ ["moduleF"])' config.json
The following filter should perform the update you want:
(.git_namespaces[] | select(.name=="NamespaceY").modules) += ["moduleF"]
Note that the initial '.|' in your attempt is not needed; that "NamespaceY" is capitalized in config.json; that the parens as shown are the keys to success; and that += can be used here.
One way to write back to the original file would perhaps be to use 'sponge'; other possibilities are discussed on the jq FAQ https://github.com/stedolan/jq/wiki/FAQ
I would like to delete all the resloved from a npm shrinwrap json file. this is causing a problem when running npm install on other machine.
"cssstyle": {
"version": "0.2.37",
"from": "cssstyle#>=0.2.29 <0.3.0",
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-0.2.37.tgz"
},
"dashdash": {
"version": "1.14.0",
"from": "dashdash#>=1.12.0 <2.0.0",
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.0.tgz",
"dependencies": {
"assert-plus": {
"version": "1.0.0",
"from": "assert-plus#>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz"
}
}
},
"debug": {
"version": "2.2.0",
"from": "debug#>=2.2.0 <3.0.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz"
}
How can I delete the resolved key from all the file
I'm using the pattern :
jq 'del(.resolved)' file.json
In my opinion, the simplest approach to this kind of problem is to use walk/1:
walk(if type == "object" and has("resolved") then del(.resolved) else . end)
If your jq does not have walk/1 (which was only included as a builtin after the release of jq 1.5), then simply add its definition (easily available on the web) before the above line, or perhaps include it in your ~/.jq file.