Skip to content
/ jsq Public

A command-line JSON processor with JavaScript syntax

License

Notifications You must be signed in to change notification settings

pdonias/jsq

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

88 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

jsq

A command-line JSON processor like jq, but using familiar JavaScript syntax instead of a custom DSL.

Install

npm install --global jsq-cli

Usage

$ <command> | jsq <expression>

Access properties like jq:

$ echo '{ "foo": 1, "bar": 2 }' | jsq .foo
1

Or use full JavaScript expressions:

$ echo '{ "foo": 1, "bar": 2 }' | jsq 'Object.entries(.).map(entry => entry.join("=")).join("; ")'
foo=1; bar=2

Input

A JSON input can be piped into jsq and optionally named with --as:

$ echo '{ "foo": 1, "bar": 2 }' | jsq --as myInput 'myInput.foo'
1

Inputs can also be declared by passing --input.<name> options:

$ jsq --input.myInput '{ "foo": 1, "bar": 2 }' 'myInput.foo'
1

jsq also supports NDJSON from stdin. The NDJSON data is converted to an array and injected into the expression like a normal piped input.

Tip

Early interrupt an NDJSON input with Ctrl + C. Use --no-buffer on curl to force it to write immediately to stdout.

Expression

You can process the input(s) with a JavaScript expression. The expression may contain multiple statements, the value of the last statement is the output of jsq.

Whether or not you named the piped input with --as, it is also accessible in the expression as . (also aliased as _ if you want to avoid invalid JavaScript syntax):

$ echo '{ "foo": 1, "bar": 2 }' | jsq 'Object.keys(.).join(", ")'
foo, bar

$ echo '{ "foo": 1, "bar": 2 }' | jsq 'a = .foo; b = _.bar; a + b'
3

Important

If the last statement is an inline object, wrap it with ({ ... }) so that NodeJS doesn't interpret it as a block.

Output

If you don't call echo in the expression, jsq outputs the value of the last statement in the expression. If you don't pass an expression, it will simply format and pretty-print the piped input.

  • Use --depth <n> if the output is an object to configure how deep the object will be rendered.
  • Use --json to output raw JSON instead of pretty-print.

Functions

Use --fn.<name> <cmd> to declare shell-based functions that will also be accessible from within the expression in the global scope. The command may contain {0}, {1}, {2}, ... that will be replaced by the function's arguments when you call it in the expression. {} is an alias for {0}.

$ echo '{ "foo": 1, "bar": 2 }' | jsq --fn.apiFetch 'curl https://api.com/{0}/{1}' 'apiFetch("user", .foo)'
{ userid: 1, firstname: 'John', lastname: 'Doe' }
  • Use --resolve <cmd> or -r <cmd> as a shorthand for --fn.resolve <cmd>.
  • The output of functions is automatically JSON.parsed if possible.
  • Escape a {i} with \{i}.

Utils

Some utility functions are also exposed in the global scope.

echo

By default, the output of jsq is the value of the last JavaScript statement in the expression. However, if you want to declare the output explicitly, you can call the function echo in the expression. You may call it one or multiple times. If you call it at least once, jsq will ignore the value of the last statement and let you manage the output. If you call it multiple times, the values will be printed to stdout separated by newlines.

$ echo '{ "foo": 1, "bar": 2 }' | jsq 'Object.entries(.).forEach(([key, value]) => echo(`${key}: ${value}`))'
foo: 1
bar: 2

console.log/console.error

You may call console.log and console.error to debug your expression. It won't pollute the stdout as both are redirected to stderr.

exit

You may call exit explicitly to end the jsq process early. Pass the exit code (or a boolean) as an argument. This can be useful to conditionally return early.

lodash

All lodash functions are available in the global context, except if one or more of your named inputs or functions override them.

$ echo '{ "foo": 1, "bar": 2 }' | jsq 'invert(.)'
{ '1': 'foo', '2': 'bar' }

Cache

By default, jsq remembers:

  • inputs: this is convenient if the command you pipe into jsq takes a while to return. If you need to run mutilple expressions on the same input, you only need to pipe it the first time.
  • outputs: access the result of the previous run with _ans or save the result of a run as a named global variable with --save-as <name> to reuse the result of a run in the next ones.
  • functions: similarly, if you configure some helper functions, you can reuse them in later calls without redeclaring them.
# Pipe the input once
$ echo '{ "foo": 1, "bar": 2 }' | jsq
{ foo: 1, bar: 2 }

# and jsq remembers it on the following runs
$ jsq .foo
1

# Declare functions with --fn
$ jsq --fn.apiFetch 'curl https://api.com/{0}/{1}' 'apiFetch("user", _ans)'
{ userid: 1, firstname: 'John', lastname: 'Doe' }

# and jsq remembers them too
$ jsq 'apiFetch("user", .bar)'
{ userid: 2, firstname: 'Jane', lastname: 'Doe' }

# jsq also remembers the last result as _ans, give it a proper name with --save-as
$ jsq _ans --save-as jane
{ userid: 2, firstname: 'Jane', lastname: 'Doe' }
  • Use --ls-cache to see the content of the cache.
  • Use --clear-cache to delete everything from the cache.
  • Use --no-cache to ignore cache completely for the current run (no read, no write)

Examples

Outputs are omitted for clarity

  1. Digging through a JSON
$ curl https://api.com/users | jsq # Pipe an input
$ jsq 'find(., { firstname: "John", lastname: "Doe" })' # Find an item with Lodash's find
$ jsq _ans | jsq # To work on that object, make it the new main input
$ jsq .friends --save-as friends # Save John's friends into a variable
$ jsq 'friends.forEach(f => { if (f.age > .age) { echo(f.firstname) } })' # Print friends that are older than John
  1. Using functions and variables
$ curl https://api.com/ids | jsq --as userIds # Pipe an input and name it
$ jsq --resolve 'curl https://api.com/user/{}' # Tell jsq how to resolve a user ID
$ jsq --fn.read 'cat {}' # Tell jsq how to resolve a filename
$ jsq 'userIds.map(resolve).map(user => read(user.tokenFile)).join("\n")' # Use functions and variables

About

A command-line JSON processor with JavaScript syntax

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project