1FX(1) User Commands FX(1)
2
3
4
6 fx - command-line JSON processing tool
7
9 fx [code ...]
10
11 fx can work in two modes: cli and interactive. To start interactive
12 mode pipe any JSON into fx:
13 $ curl ... | fx
14 Or you can pass a filename as the first parameter:
15 $ fx my.json
16 If any argument was passed, fx will apply it and prints to stdout.
17
19 Start interactive mode without passing any arguments.
20 $ curl ... | fx
21 Or by passing filename as first argument.
22 $ fx data.json
23 Pass a few JSON files.
24 $ cat foo.json bar.json baz.json | fx .message
25 Use full power of JavaScript.
26 $ curl ... | fx '.filter(x => x.startsWith("a"))'
27 Access all lodash (or ramda, etc) methods by using .fxrc file.
28 $ curl ... |
29 > fx '_.groupBy("commit.committer.name")' '_.mapValues(_.size)'
30 Update JSON using spread operator.
31 $ echo '{"count": 0}' | fx '{...this, count: 1}'
32 {
33 "count": 1
34 }
35 Extract values from maps.
36 $ fx commits.json | fx .[].author.name
37 Print formatted JSON to stdout.
38 $ curl ... | fx .
39 Pipe JSON logs stream into fx.
40 $ kubectl logs ... -f | fx .message
41 And try this:
42 $ fx --life
43
44 USAGE
45 ANONYMOUS FUNCTION
46 Use an anonymous function as reducer which gets JSON and pro‐
47 cesses it:
48 $ echo '{"foo": [{"bar": "value"}]}' | fx 'x => x.foo[0].bar'
49 value
50
51 BINDING
52 If you don't pass anonymous function param => ..., code will be
53 automatically transformed into anonymous function. And you can
54 get access to JSON by this keyword:
55 $ echo '{"foo": [{"bar": "value"}]}' | fx 'this.foo[0].bar'
56 value
57
58 DOT It is possible to omit this keyword:
59 $ echo '{"foo": [{"bar": "value"}]}' | fx .foo[0].bar
60 value
61 If a single dot is passed, the input JSON will be formatted but
62 otherwise unaltered:
63 $ echo '{"foo": "bar"}' | fx .
64 {
65 "foo": "bar"
66 }
67
68 MAP One of the frequent operations is mapping some function on an
69 array. For example, to extract some values.
70 [
71 {
72 "author": {
73 "name": "antonmedv"
74 }
75 },
76 {...},
77 ...
78 ]
79 And we want to collect names of each object in the array. We
80 can do this by mapping anonymous function:
81 $ cat ... | fx '.map(x => x.author.name)'
82 Or we can do the same by using jq(1)-like syntax:
83 $ cat ... | fx .[].author.name
84 [
85 "antonmedv",
86 ...
87 ]
88 Note what [] can be applied to map object values.
89 $ echo '{"foo": 1, "bar": 2}' | fx .[]
90 [1, 2]
91
92 CHAINING
93 You can pass any number of anonymous functions for reducing
94 JSON:
95 $ echo '{"foo": [{"bar": "value"}]}' |
96 > fx 'x => x.foo' 'this[0]' 'this.bar'
97 value
98
99 UPDATING
100 You can update existing JSON using the spread operator:
101 $ echo '{"count": 0}' | fx '{...this, count: 1}'
102 {
103 "count": 1
104 }
105
106 EDIT-IN-PLACE
107 fx provides a function save which will save everything in place
108 and return saved object. This function can be only used with
109 filename as first argument to fx command.
110
111 Usage:
112 $ fx data.json '{...this, count: this.count+1}' save .count
113
114 USING PACKAGES
115 Use any npm package by installing it globally:
116 $ npm install -g lodash
117 $ cat package.json |
118 > fx 'require("lodash").keys(this.dependencies)'
119
120 FORMATTING
121 If you need output other than JSON (for example arguments for xargs),
122 do not return anything from the reducer. undefined value is printed
123 into stderr by default.
124 echo '[]' | fx 'void 0'
125 undefined
126
127 echo '[1,2,3]' |
128 fx 'this.forEach(x => console.log(+x))' 2>/dev/null |
129 xargs echo
130 1 2 3
131
132 STREAMING MODE
133 fx supports line-delimited JSON and concatenated JSON streaming.
134 $ kubectl logs ... | fx .message
135 Note what is object lacks message field, undefined will be printed to
136 stderr. This is useful to see if you are skipping some objects. But
137 if you want to hide them, redirect stderr to /dev/null.
138
139 FILTERING
140 Sometimes it is necessary to omit some messages in JSON stream,
141 or select only specified log messages. For this purpose, fx has
142 special helpers select/filter, pass function into it to se‐
143 lect/filter JSON messages.
144 $ kubectl logs ... |
145 > fx 'select(x => x.status == 500)' .message
146
147 $ kubectl logs ... |
148 > fx 'filter(x => x.status < 499)' .message
149
150 If filter/select overridden in .fxrc you still able to access
151 them with prefix: std.select(cb) or std.filter(cd)
152
154 USING .fxrc
155 Create .fxrc in $HOME directory, and require any packages or define
156 global functions.
157
158 For example, access all lodash methods without _ prefix. Put in your
159 .fxrc file:
160 Object.assign(global, require('lodash/fp'))
161 And now you will be able to call all lodash methods. For example, see
162 who's been committing to react recently:
163 curl 'https://api.github.com/repos/facebook/react/commits?per_page=100' |
164 fx 'groupBy("commit.author.name")' 'mapValues(size)' toPairs \
165 'sortBy(1)' reverse 'take(10)' fromPairs
166 See the ENVIRONMENT section.
167
168 QUERY LANGUAGE
169 If you want to use query language, for example jsonata
170 ⟨https://jsonata.org/⟩ you can use helper function like this:
171 global.jsonata = expr => require('jsonata')(expr).evaluate
172 And use it like this:
173 curl ... |
174 fx 'jsonata("$sum(Order.Product.(Price * Quantity))")'
175 Instead you can create next alias in .bashrc file:
176 alias jsonata='FX_APPLY=jsonata fx'
177 And now all code arguments to jsonata will be passed through
178 jsonata helper. And now you can use it like this:
179 curl ... | jsonata '$sum(Order.Product.(Price * Quantity))'
180
181 INTERACTIVE MODE
182 Click on fields to expand or collapse JSON tree, use mouse wheel to
183 scroll view.
184
185 Next commands available in interactive mode:
186
187 ┌───────────────────┬──────────────────────────────────────────────┐
188 │ Key │ Command │
189 ├───────────────────┼──────────────────────────────────────────────┤
190 │q or Esc or Ctrl+c │ Exit │
191 ├───────────────────┼──────────────────────────────────────────────┤
192 │up or k │ Move cursor up │
193 ├───────────────────┼──────────────────────────────────────────────┤
194 │left or h │ Collapse │
195 ├───────────────────┼──────────────────────────────────────────────┤
196 │right or l │ Expand │
197 ├───────────────────┼──────────────────────────────────────────────┤
198 │Shift+right or L │ Expand all under cursor │
199 ├───────────────────┼──────────────────────────────────────────────┤
200 │Shift+left or K │ Collapse all under cursor │
201 ├───────────────────┼──────────────────────────────────────────────┤
202 │e │ Expand all │
203 ├───────────────────┼──────────────────────────────────────────────┤
204 │E │ Collapse all │
205 ├───────────────────┼──────────────────────────────────────────────┤
206 │g │ Scroll to top │
207 ├───────────────────┼──────────────────────────────────────────────┤
208 │G │ Scroll to bottom │
209 ├───────────────────┼──────────────────────────────────────────────┤
210 │. │ Edit filter │
211 ├───────────────────┼──────────────────────────────────────────────┤
212 │/ │ Search │
213 ├───────────────────┼──────────────────────────────────────────────┤
214 │n │ Find next │
215 ├───────────────────┼──────────────────────────────────────────────┤
216 │p │ Exit and print JSON to stdout │
217 ├───────────────────┼──────────────────────────────────────────────┤
218 │P │ Exit and print fully expanded JSON to stdout │
219 └───────────────────┴──────────────────────────────────────────────┘
220 These commands are available when editing the filter:
221
222 ┌────────┬─────────────────────┐
223 │ Key │ Command │
224 ├────────┼─────────────────────┤
225 │Enter │ Apply filter │
226 ├────────┼─────────────────────┤
227 │Ctrl+u │ Clear filter │
228 ├────────┼─────────────────────┤
229 │Ctrl+w │ Delete last part │
230 ├────────┼─────────────────────┤
231 │up/down │ Select autocomplete │
232 └────────┴─────────────────────┘
233 SEARCHING
234 Press / and type regexp pattern to search in current JSON.
235 Search work with currently applied filter.
236
237 Examples of pattern and corresponding regexp:
238
239 ┌───────────┬───────────┐
240 │ Pattern │ RegExp │
241 ├───────────┼───────────┤
242 │/apple │ /apple/ig │
243 ├───────────┼───────────┤
244 │/apple/+u │ /apple/ │
245 ├───────────┼───────────┤
246 │/apple/u+w │ /apple/u │
247 ├───────────┼───────────┤
248 │\w+ │ /\w+/ig │
249 └───────────┴───────────┘
250 SELECTING TEXT
251 You may found what you can't just select text in fx. This is
252 due the fact that all mouse events redirected to stdin. To be
253 able select again you need instruct your terminal not to do it.
254 This can be done by holding special keys while selecting:
255
256 ┌─────────────┬───────────────┐
257 │ Key │ Terminal │
258 ├─────────────┼───────────────┤
259 │Option+Mouse │ iTerm2, Hyper │
260 ├─────────────┼───────────────┤
261 │Fn+Mouse │ Terminal.app │
262 ├─────────────┼───────────────┤
263 │Shift+Mouse │ Linux │
264 └─────────────┴───────────────┘
265 Note what you can press p/P to print everything to stdout and
266 select if there.
267
268 MEMORY USAGE
269 You may find that sometimes, on really big JSON files, fx prints an er‐
270 ror message like this:
271 FATAL ERROR: JavaScript heap out of memory
272 V8 limits memory usage to around 2 GB by default. You can increase the
273 limit by putting this line in your .profile:
274 export NODE_OPTIONS='--max-old-space-size=8192'
275
277 To be able require global modules make sure you have correct NODE_PATH
278 env variable.
279
281 BASIC EXAMPLES
282 $ echo '{"key": "value"}' | fx 'x => x.key'
283 value
284
285 $ echo '{"key": "value"}' | fx .key
286 value
287
288 $ echo '[1,2,3]' | fx 'this.map(x => x * 2)'
289 [2, 4, 6]
290
291 $ echo '{"items": ["one", "two"]}' | fx 'this.items' 'this[1]'
292 two
293
294 $ echo '{"count": 0}' | fx '{...this, count: 1}'
295 {"count": 1}
296
297 $ echo '{"foo": 1, "bar": 2}' | fx ?
298 ["foo", "bar"]
299
300 OTHER EXAMPLES
301 Convert object to array:
302 $ cat package.json | fx 'Object.keys(this.dependencies)'
303 Or by two functions:
304 $ cat package.json | fx .dependencies Object.keys
305 By the way, fx has shortcut for Object.keys. Previous example can be
306 rewritten as:
307 $ cat package.json | fx .dependencies ?
308
310 jq(1)
311
312
313
314 February 2021 FX(1)