.\" Automatically generated by Pandoc 2.2.1 .\" .TH "GRON" "1" "July 2018" "0.6.0" "User Commands" .hy .SH NAME .PP \f[B]gron\f[] \- transform JSON into discrete, greppable assignments .SH SYNOPSIS .PP \f[B]gron\f[] [\f[I]OPTIONS\f[]] [\f[I]FILE\f[]|\f[I]URL\f[]] .PP \f[B]gron\f[] \f[B]\[en]\-version\f[] .PP \f[B]gron\f[] \f[B]\[en]\-help\f[] .SH DESCRIPTION .PP \f[B]gron\f[] transforms JSON into discrete assignments to make it easier to \f[B]grep\f[] for what you want and see the absolute `path' to it. It eases the exploration of APIs that return large blobs of JSON but have terrible documentation. .SH OPTIONS .TP .B \-u, \-\-ungron reverse the operation (turn assignments back into JSON) .RS .RE .TP .B \-c, \-\-colorize colorize output (default on tty) .RS .RE .TP .B \-m, \-\-monochrome monochrome (don't colorize output) .RS .RE .TP .B \-s, \-\-stream treat each line of input as a separate JSON object .RS .RE .TP .B \-k, \-\-insecure disable certificate validation .RS .RE .TP .B \-j, \-\-json represent gron data as JSON stream .RS .RE .TP .B \-\-no\-sort don't sort output (faster) .RS .RE .SH BASIC USAGE .PP Get JSON from a file: .IP .nf \f[C] $\ gron\ testdata/two.json\ json\ =\ {}; json.contact\ =\ {}; json.contact.email\ =\ "mail\@tomnomnom.com"; json.contact.twitter\ =\ "\@TomNomNom"; json.github\ =\ "https://github.com/tomnomnom/"; json.likes\ =\ []; json.likes[0]\ =\ "code"; json.likes[1]\ =\ "cheese"; json.likes[2]\ =\ "meat"; json.name\ =\ "Tom"; \f[] .fi .PP From a URL: .IP .nf \f[C] $\ gron\ http://headers.jsontest.com/ json\ =\ {}; json.Host\ =\ "headers.jsontest.com"; json["User\-Agent"]\ =\ "gron/0.1"; json["X\-Cloud\-Trace\-Context"]\ =\ "6917a823919477919dbc1523584ba25d/11970839830843610056"; \f[] .fi .PP Or from \f[C]STDIN\f[]: .IP .nf \f[C] $\ curl\ \-s\ http://headers.jsontest.com/\ |\ gron json\ =\ {}; json.Accept\ =\ "*/*"; json.Host\ =\ "headers.jsontest.com"; json["User\-Agent"]\ =\ "curl/7.43.0"; json["X\-Cloud\-Trace\-Context"]\ =\ "c70f7bf26661c67d0b9f2cde6f295319/13941186890243645147"; \f[] .fi .PP Grep for something and easily see the path to it: .IP .nf \f[C] $\ gron\ testdata/two.json\ |\ grep\ twitter json.contact.twitter\ =\ "\@TomNomNom"; \f[] .fi .PP \f[C]gron\f[] makes diffing JSON easy too: .IP .nf \f[C] $\ diff\ <(gron\ two.json)\ <(gron\ two\-b.json) 3c3 <\ json.contact.email\ =\ "mail\@tomnomnom.com"; \-\-\- >\ json.contact.email\ =\ "contact\@tomnomnom.com"; \f[] .fi .PP The output of \f[C]gron\f[] is valid JavaScript: .IP .nf \f[C] $\ gron\ testdata/two.json\ >\ tmp.js $\ echo\ "console.log(json);"\ >>\ tmp.js $\ nodejs\ tmp.js {\ contact:\ {\ email:\ \[aq]mail\@tomnomnom.com\[aq],\ twitter:\ \[aq]\@TomNomNom\[aq]\ }, \ \ github:\ \[aq]https://github.com/tomnomnom/\[aq], \ \ likes:\ [\ \[aq]code\[aq],\ \[aq]cheese\[aq],\ \[aq]meat\[aq]\ ], \ \ name:\ \[aq]Tom\[aq]\ } \f[] .fi .PP It's also possible to obtain the \f[C]gron\f[] output as JSON stream via the \f[C]\-\-json\f[] switch: .IP .nf \f[C] $\ curl\ \-s\ http://headers.jsontest.com/\ |\ gron\ \-\-json [[],{}] [["Accept"],"*/*"] [["Host"],"headers.jsontest.com"] [["User\-Agent"],"curl/7.43.0"] [["X\-Cloud\-Trace\-Context"],"c70f7bf26661c67d0b9f2cde6f295319/13941186890243645147"] \f[] .fi .SH UNGRONNING .PP \f[C]gron\f[] can also turn its output back into JSON: .IP .nf \f[C] $\ gron\ testdata/two.json\ |\ gron\ \-u { \ \ "contact":\ { \ \ \ \ "email":\ "mail\@tomnomnom.com", \ \ \ \ "twitter":\ "\@TomNomNom" \ \ }, \ \ "github":\ "https://github.com/tomnomnom/", \ \ "likes":\ [ \ \ \ \ "code", \ \ \ \ "cheese", \ \ \ \ "meat" \ \ ], \ \ "name":\ "Tom" } \f[] .fi .PP This means you use can use \f[C]gron\f[] with \f[C]grep\f[] and other tools to modify JSON: .IP .nf \f[C] $\ gron\ testdata/two.json\ |\ grep\ likes\ |\ gron\ \-\-ungron { \ \ "likes":\ [ \ \ \ \ "code", \ \ \ \ "cheese", \ \ \ \ "meat" \ \ ] } \f[] .fi .PP or .IP .nf \f[C] $\ gron\ \-\-json\ testdata/two.json\ |\ grep\ likes\ |\ gron\ \ \-\-json\ \-\-ungron { \ \ "likes":\ [ \ \ \ \ "code", \ \ \ \ "cheese", \ \ \ \ "meat" \ \ ] } \f[] .fi .PP To preserve array keys, arrays are padded with \f[C]null\f[] when values are missing: .IP .nf \f[C] $\ gron\ testdata/two.json\ |\ grep\ likes\ |\ grep\ \-v\ cheese json.likes\ =\ []; json.likes[0]\ =\ "code"; json.likes[2]\ =\ "meat"; ▶\ gron\ testdata/two.json\ |\ grep\ likes\ |\ grep\ \-v\ cheese\ |\ gron\ \-\-ungron { \ \ "likes":\ [ \ \ \ \ "code", \ \ \ \ null, \ \ \ \ "meat" \ \ ] } \f[] .fi .SH ADVANCED USAGE .PP Although gron's primary purpose is API discovery, when combined with other tools like \f[C]grep\f[] it can do some interesting things. .PP As an exercise, let's try to mimic some of the examples from the jq tutorial (https://stedolan.github.io/jq/tutorial/). .RS .PP Disclaimer: munging data on the command line with gron can be useful, but using tools like \f[C]grep\f[] and \f[C]sed\f[] to manipulate the data is error\-prone and shouldn't be relied on in scripts. .RE .PP Get the last 5 commits from the gron repo: .IP .nf \f[C] $\ gron\ "https://api.github.com/repos/tomnomnom/gron/commits?per_page=5" json\ =\ []; json[0]\ =\ {}; json[0].author\ =\ {}; json[0].author.avatar_url\ =\ "https://avatars.githubusercontent.com/u/58276?v=3"; json[0].author.events_url\ =\ "https://api.github.com/users/tomnomnom/events{/privacy}"; \&... json[4].parents[0].html_url\ =\ "https://github.com/tomnomnom/gron/commit/cbcad2299e55c28a9922776e58b2a0b5a0f05016"; json[4].parents[0].sha\ =\ "cbcad2299e55c28a9922776e58b2a0b5a0f05016"; json[4].parents[0].url\ =\ "https://api.github.com/repos/tomnomnom/gron/commits/cbcad2299e55c28a9922776e58b2a0b5a0f05016"; json[4].sha\ =\ "91b204972e63a1166c9d148fbbfd839f8697f91b"; json[4].url\ =\ "https://api.github.com/repos/tomnomnom/gron/commits/91b204972e63a1166c9d148fbbfd839f8697f91b"; \f[] .fi .PP To make the rest of this a little more readable, let's add an alias for that: .IP .nf \f[C] $\ alias\ ggh=\[aq]gron\ "https://api.github.com/repos/tomnomnom/gron/commits?per_page=5"\[aq] \f[] .fi .PP Extract just the first commit using \f[C]fgrep\ "json[0]"\f[]: .IP .nf \f[C] $\ ggh\ |\ fgrep\ "json[0]" json[0]\ =\ {}; json[0].author\ =\ {}; json[0].author.avatar_url\ =\ "https://avatars.githubusercontent.com/u/58276?v=3"; json[0].author.events_url\ =\ "https://api.github.com/users/tomnomnom/events{/privacy}"; json[0].author.followers_url\ =\ "https://api.github.com/users/tomnomnom/followers"; \&... json[0].parents[0].html_url\ =\ "https://github.com/tomnomnom/gron/commit/48aba5325ece087ae24ab72684851cbe77ce8311"; json[0].parents[0].sha\ =\ "48aba5325ece087ae24ab72684851cbe77ce8311"; json[0].parents[0].url\ =\ "https://api.github.com/repos/tomnomnom/gron/commits/48aba5325ece087ae24ab72684851cbe77ce8311"; json[0].sha\ =\ "7da81e29c27241c0a5c2e5d083ddebcfcc525908"; json[0].url\ =\ "https://api.github.com/repos/tomnomnom/gron/commits/7da81e29c27241c0a5c2e5d083ddebcfcc525908"; \f[] .fi .PP Get just the committer's name and the commit message using \f[C]egrep\ "(committer.name|commit.message)"\f[]: .IP .nf \f[C] $\ ggh\ |\ fgrep\ "json[0]"\ |\ egrep\ "(committer.name|commit.message)" json[0].commit.committer.name\ =\ "Tom\ Hudson"; json[0].commit.message\ =\ "Adds\ 0.1.7\ to\ changelog"; \f[] .fi .PP Turn the result back into JSON using \f[C]gron\ \-\-ungron\f[]: .IP .nf \f[C] ▶\ ggh\ |\ fgrep\ "json[0]"\ |\ egrep\ "(committer.name|commit.message)"\ |\ gron\ \-\-ungron [ \ \ { \ \ \ \ "commit":\ { \ \ \ \ \ \ "committer":\ { \ \ \ \ \ \ \ \ "name":\ "Tom\ Hudson" \ \ \ \ \ \ }, \ \ \ \ \ \ "message":\ "Adds\ 0.1.7\ to\ changelog" \ \ \ \ } \ \ } ] \f[] .fi .PP gron preserves the location of values in the JSON, but you can use \f[C]sed\f[] to remove keys from the path: .IP .nf \f[C] $\ ggh\ |\ fgrep\ "json[0]"\ |\ egrep\ "(committer.name|commit.message)"\ |\ sed\ \-r\ "s/(commit|committer)\\.//g" json[0].name\ =\ "Tom\ Hudson"; json[0].message\ =\ "Adds\ 0.1.7\ to\ changelog" \f[] .fi .PP With those keys removed, the result is a `flattened' object, which looks much cleaner when turned back into JSON with \f[C]gron\ \-\-ungron\f[]: .IP .nf \f[C] $\ ggh\ |\ fgrep\ "json[0]"\ |\ egrep\ "(committer.name|commit.message)"\ |\ sed\ \-r\ "s/(commit|committer)\\.//g"\ |\ gron\ \-\-ungron [ \ \ { \ \ \ \ "message":\ "Adds\ 0.1.7\ to\ changelog", \ \ \ \ "name":\ "Tom\ Hudson" \ \ } ] \f[] .fi .PP Removing the \f[C]fgrep\ "json[0]"\f[] from the pipeline means we do the same for all commits: .IP .nf \f[C] $\ ggh\ |\ egrep\ "(committer.name|commit.message)"\ |\ sed\ \-r\ "s/(commit|committer)\\.//g"\ |\ gron\ \-\-ungron [ \ \ { \ \ \ \ "message":\ "Adds\ 0.1.7\ to\ changelog", \ \ \ \ "name":\ "Tom\ Hudson" \ \ }, \ \ { \ \ \ \ "message":\ "Refactors\ natural\ sort\ to\ actualy\ work\ +\ be\ more\ readable", \ \ \ \ "name":\ "Tom\ Hudson" \ \ }, \&... \f[] .fi .PP To include the \f[C]html_url\f[] key for each commit's parents, all we need to do is add \f[C]parents.*html_url\f[] into our call to \f[C]egrep\f[]: .IP .nf \f[C] $\ ggh\ |\ egrep\ "(committer.name|commit.message|parents.*html_url)"\ |\ sed\ \-r\ "s/(commit|committer)\\.//g" json[0].name\ =\ "Tom\ Hudson"; json[0].message\ =\ "Adds\ 0.1.7\ to\ changelog"; json[0].parents[0].html_url\ =\ "https://github.com/tomnomnom/gron/commit/48aba5325ece087ae24ab72684851cbe77ce8311"; json[1].name\ =\ "Tom\ Hudson"; json[1].message\ =\ "Refactors\ natural\ sort\ to\ actualy\ work\ +\ be\ more\ readable"; json[1].parents[0].html_url\ =\ "https://github.com/tomnomnom/gron/commit/3eca8bf5e07151f077cebf0d942c1fa8bc51e8f2"; \&... \f[] .fi .PP To make the structure more like that in the final example in the \f[C]jq\f[] tutorial, we can use \f[C]sed\ \-r\ "s/\\.html_url//"\f[] to remove the \f[C]\&.html_url\f[] part of the path: .IP .nf \f[C] ▶\ ggh\ |\ egrep\ "(committer.name|commit.message|parents.*html_url)"\ |\ sed\ \-r\ "s/(commit|committer)\\.//g"\ |\ sed\ \-r\ "s/\\.html_url//" json[0].name\ =\ "Tom\ Hudson"; json[0].message\ =\ "Adds\ 0.1.7\ to\ changelog"; json[0].parents[0]\ =\ "https://github.com/tomnomnom/gron/commit/48aba5325ece087ae24ab72684851cbe77ce8311"; json[1].name\ =\ "Tom\ Hudson"; json[1].message\ =\ "Refactors\ natural\ sort\ to\ actualy\ work\ +\ be\ more\ readable"; json[1].parents[0]\ =\ "https://github.com/tomnomnom/gron/commit/3eca8bf5e07151f077cebf0d942c1fa8bc51e8f2"; \&... \f[] .fi .PP And, of course, the statements can be turned back into JSON with \f[C]gron\ \-\-ungron\f[]: .IP .nf \f[C] $\ ggh\ |\ egrep\ "(committer.name|commit.message|parents.*html_url)"\ |\ sed\ \-r\ "s/(commit|committer)\\.//g"\ |\ sed\ \-r\ "s/\\.html_url//"\ |\ gron\ \-\-ungron [ \ \ { \ \ \ \ "message":\ "Adds\ 0.1.7\ to\ changelog", \ \ \ \ "name":\ "Tom\ Hudson", \ \ \ \ "parents":\ [ \ \ \ \ \ \ "https://github.com/tomnomnom/gron/commit/48aba5325ece087ae24ab72684851cbe77ce8311" \ \ \ \ ] \ \ }, \ \ { \ \ \ \ "message":\ "Refactors\ natural\ sort\ to\ actualy\ work\ +\ be\ more\ readable", \ \ \ \ "name":\ "Tom\ Hudson", \ \ \ \ "parents":\ [ \ \ \ \ \ \ "https://github.com/tomnomnom/gron/commit/3eca8bf5e07151f077cebf0d942c1fa8bc51e8f2" \ \ \ \ ] \ \ }, \&... \f[] .fi .SH EXAMPLES .PP Read from a local file/network: .IP .nf \f[C] $\ gron\ /tmp/apiresponse.json $\ gron\ http://jsonplaceholder.typicode.com/users/1\ \f[] .fi .PP Retrieve remote JSON and pipe through gron: .IP .nf \f[C] $\ curl\ \-s\ http://jsonplaceholder.typicode.com/users/1\ |\ gron \f[] .fi .PP Flatten and filter JSON through gron, before turning result back into JSON: .IP .nf \f[C] $\ gron\ http://jsonplaceholder.typicode.com/users/1\ |\ grep\ company\ |\ gron\ \-\-ungron \f[] .fi .SH TIPS .PP It's recommended that you alias \f[C]ungron\f[] or \f[C]norg\f[] (or both!) to \f[C]gron\ \-\-ungron\f[]. Put something like this in your shell profile (e.g.\ in \f[C]~/.bashrc\f[]): .IP .nf \f[C] alias\ norg="gron\ \-\-ungron" alias\ ungron="gron\ \-\-ungron" \f[] .fi .PP Or you could create a shell script in your $PATH named `ungron` or `norg` to affect all users: ``` gron \-\-ungron "$\@" ``` .SH EXIT STATUS .PP 0 OK .PD 0 .P .PD 1 Failed to open file .PD 0 .P .PD 2 Failed to read input .PD 0 .P .PD 3 Failed to form statements .PD 0 .P .PD 4 Failed to fetch URL .PD 0 .P .PD 5 Failed to parse statements .PD 0 .P .PD 6 Failed to encode JSON .SH REPORTING BUGS .PP Upstream bug tracker: https://github.com/tomnomnom/gron/issues .SH COPYRIGHT .PP Copyright (c) 2016 Tom Hudson .SH AUTHOR .PP This manual page is based on the gron documentation. It was created by Nick Morrott for the Debian GNU/Linux system, but may be used by others .SH SEE ALSO .PP \f[B]grep(1)\f[], \f[B]ack(1)\f[], \f[B]ag(1)\f[], \f[B]rg(1)\f[], \f[B]jq(1)\f[]