Discover how to use fx effectively, a JSON manipulation command line tool
fx is a popular JSON manipulation command line tool. It can be installed via npm, brew or downloaded as a standalone binary.
npm i -g fx
fx comes in handy with a curl command. If you have some API which returns JSON and you want to dig into the structure or just see it, pipe JSON into fx.
curl https://swapi.co/api/planets/ | fx
fx has a really neat interactive mode for digging into JSON.
You can use your mouse 🐁 or arrow keys for navigating JSON. fx supports folds, click on a filed to expand it or press ➡️ right arrow to expand. Press e
key to expand all fields recursively. Press Shift + e
to collapse everything back.
Interactive digger
While in interactive mode press .
(dot) key. You will see a cursor at the end of your terminal window with a popup of JSON fields. Use arrow keys to choose a field and press enter, continue until you find needed data. If you need to go back one level press Ctrl + w
to erase last path segment. This command accurately removes last part next to .
or [
bracket. If you want to start over, press Ctrl + u
.
BTW, in this field you can write any JavaScript expression not only path expressions.
Pretty printing
Sometimes you don’t need the interactive digger and want to pretty print JSON to stdout. This is can be done by adding .
argument to fx command.
curl https://swapi.co/api/planets/1/ | fx .
Or to print only part of JSON specify the desired path.
curl https://swapi.co/api/planets/1/ | fx .films
Requesting API each time is time consuming, what if we save it to a disk first?
curl https://swapi.co/api/planets/1/ > data.json
Now we can pass the file as the first argument to fx.
fx data.json .films
Each argument to fx can be some JavaScript function, let’s create .fxrc
file there we put useful function and snippets for reuse.
Snippets
Let’s add the power of lodash to our command line. Install lodash globally.
npm i -g lodash
Create .fxrc
file into your home directory. And put the next line into it.
Object.assign(global, require('lodash/fp'))
Now you will be able to call all lodash methods even without using _
prefix. For example, see who’s been committing to react recently:
curl 'https://api.github.com/repos/facebook/react/commits' \
| fx 'groupBy("commit.author.name")' 'mapValues(size)' \
toPairs 'sortBy(1)' reverse 'take(10)' fromPairs
Let’s count the usage of different words in commit messages.
curl 'https://api.github.com/repos/facebook/react/commits' \
| fx 'map("commit.message")' 'map(words)' flatten \
'map(lowerCase)' 'groupBy(identity)' 'mapValues(size)' \
toPairs 'sortBy(1)' reverse 'take(30)' fromPairs
Adding your own snippets
This is pretty easy as well. For example, I have one API which requires documents to be base64 encoded. Let’s see how it’s can be done.
Create some function and assign in to global. For example, a base64 snippet.
global.base64 = str => Buffer.from(str).toString('base64')
Now I’m able to do something like this:
fx data.json '{value: base64(JSON.stringify(this))}' | curl -X POST
Or if split to separate functions:
fx data.json JSON.stringify base64 '{value: this}' | curl -X POST
Edit in-place
With fx you can easily modify JSON objects by using ...
spread operator.
echo '{"count": 0}' | fx '{...this, count: this.count+1}'
But if you try to modify a file and save on a disk in one command, you corrupt your file.
fx data.json '{...this, count: this.count+1}' > data.json
This is common to all command line. For example, sed has a special flag for edit in-place sed -i
.
fx has a special function called save
which saves JSON to file and returns it.
fx data.json '{...this, count: 1}' save
We can even output the modified field while saving on the disk.
fx data.json '{...this, count: this.count+1}' save .count
Using xargs
fx prints strings into stdout as “raw” strings without quotes so this can be useful for making fx filters talk to non-JSON-based systems. But if we want to pass a list of arguments? Next snippet comes in handy.
global.list = json => (json.forEach(x => console.log(x)), undefined)
By default, undefined
prints into stderr so this won’t affect our pipes.
curl https://swapi.co/api/planets/1/ | fx .films list | xargs curl
Searching JSON
fx supports interactive JSON searching. Press /
can type your pattern to search.
To jump to the next pattern match press n
. You can apply filter with .
as well. fx works great with both of them simultaneously.
But what if we want something different?
I think JavaScript is the best language for working with JSON (an acronym to JavaScript Object Notation). What in we want to find all occurrences of some pattern and print it to stdout? We can create our own search snippet. So let’s create a search snippet!
Now we can recursively search our JSON!
fx data.json 'find(/fix/i)'
And even better with using previous snippet list
you can print founded parts.
fx data.json 'find(/fix/i)' list | xargs -L1 fx data.json
Learn by examples
Update JSON using the spread operator.
$ echo '{"count": 0}' | fx '{...this, count: 1}'
{
"count": 1
}
Extract values from maps.
fx commits.json | fx .[].author.name
The last example actually the same as this.map(x => x.author.name)
but this syntax also can be used on objects and be nested.
fx .comments[].authors[].names
The next examples all do the same thing.
fx 'x => x.foo[0].bar'
fx 'this.foo[0].bar'
fx '.foo[0].bar'
fx '.foo[0]' | fx '.bar'
fx '.foo[0]' '.bar'
If you need to run interactive mode only on part of JSON, pipe it again to fx.
cat data.json | fx .value[0].nested | fx
Tables
Format JSON into a nice table via cli-table3. Add next spinnet into your .fxrc
file:
Now let’s try to output a list of commits authors in a nice table:
fx commits.json .[].commit.author table
And now we will get this nice looking table.
Streaming
fx supports JSON streaming as well. You can use it for parsing logs, etc.
kubectl logs ... | fx .message
Select(or filter) only desired messages by prepending select
helper.
kubectl logs ... | fx 'select(x => x.level == "info")' .message
Or just concatenate a few JSON files with cat
and pipe to fx.
cat *.json | fx .length
🎨 Themes support
fx supports themes as well. You can change colors and indent.
global.FX_STYLE_SPACE = 4
Two awesome themes for fx are below:
- fx-theme-monokai — monokai theme
- fx-theme-night — night theme
I hope you enjoy using fx!