.\"t .\" Automatically generated by Pandoc 2.9.1.1 .\" .TH "JO" "1" "" "User Manuals" "" .hy .SH NAME .PP jo - JSON output from a shell .SH SYNOPSIS .PP jo [-p] [-a] [-B] [-e] [-v] [-V] [-d keydelim] [\[en]] [ [-s|-n|-b] word \&...] .SH DESCRIPTION .PP \f[I]jo\f[R] creates a JSON string on \f[I]stdout\f[R] from _word_s given it as arguments or read from \f[I]stdin\f[R]. Without option \f[C]-a\f[R] it generates an object whereby each \f[I]word\f[R] is a \f[C]key=value\f[R] (or \f[C]key\[at]value\f[R]) pair with \f[I]key\f[R] being the JSON object element and \f[I]value\f[R] its value. \f[I]jo\f[R] attempts to guess the type of \f[I]value\f[R] in order to create number (using \f[I]strtod(3)\f[R]), string, or null values in JSON. .PP \f[I]jo\f[R] normally treats \f[I]key\f[R] as a literal string value. If the \f[C]-d\f[R] option is specified, \f[I]key\f[R] will be interpreted as an \f[I]object path\f[R], whose individual components are separated by the first character of \f[I]keydelim\f[R]. .PP \f[I]jo\f[R] treats \f[C]key\[at]value\f[R] specifically as boolean JSON elements: if the value begins with \f[C]T\f[R], \f[C]t\f[R], or the numeric value is greater than zero, the result is \f[C]true\f[R], else \f[C]false\f[R]. A missing or empty value behind the colon results in a \f[C]null\f[R] JSON element. .PP \f[I]jo\f[R] creates an array instead of an object when \f[C]-a\f[R] is specified. .PP When the \f[C]:=\f[R] operator is used in a \f[I]word\f[R], the name to the right of \f[C]:=\f[R] is a file containing JSON which is parsed and assigned to the key left of the operator. The file may be specified as \f[C]-\f[R] to read from \f[I]jo\f[R]\[cq]s standard input. .SH TYPE COERCION .PP \f[I]jo\f[R]\[cq]s type guesses can be overridden on a per-word basis by prefixing \f[I]word\f[R] with \f[C]-s\f[R] for \f[I]string\f[R], \f[C]-n\f[R] for \f[I]number\f[R], or \f[C]-b\f[R] for \f[I]boolean\f[R]. The list of _word_s \f[I]must\f[R] be prefixed with \f[C]--\f[R], to indicate to \f[I]jo\f[R] that there are no more global options. .PP Type coercion works as follows: .PP .TS tab(@); l l l l l. T{ word T}@T{ -s T}@T{ -n T}@T{ -b T}@T{ default T} _ T{ a= T}@T{ \[lq]a\[rq]:\[dq]\[dq] T}@T{ \[lq]a\[rq]:0 T}@T{ \[lq]a\[rq]:false T}@T{ \[lq]a\[rq]:null T} T{ a=string T}@T{ \[lq]a\[rq]:\[lq]string\[rq] T}@T{ \[lq]a\[rq]:6 T}@T{ \[lq]a\[rq]:true T}@T{ \[lq]a\[rq]:\[lq]string\[rq] T} T{ a=\[dq]quoted\[dq] T}@T{ \[lq]a\[rq]:\[lq]\[dq]quoted\[dq]\[rq] T}@T{ \[lq]a\[rq]:8 T}@T{ \[lq]a\[rq]:true T}@T{ \[lq]a\[rq]:\[lq]\[dq]quoted\[dq]\[rq] T} T{ a=12345 T}@T{ \[lq]a\[rq]:\[lq]12345\[rq] T}@T{ \[lq]a\[rq]:12345 T}@T{ \[lq]a\[rq]:true T}@T{ \[lq]a\[rq]:12345 T} T{ a=true T}@T{ \[lq]a\[rq]:\[lq]true\[rq] T}@T{ \[lq]a\[rq]:1 T}@T{ \[lq]a\[rq]:true T}@T{ \[lq]a\[rq]:true T} T{ a=false T}@T{ \[lq]a\[rq]:\[lq]false\[rq] T}@T{ \[lq]a\[rq]:0 T}@T{ \[lq]a\[rq]:false T}@T{ \[lq]a\[rq]:false T} T{ a=null T}@T{ \[lq]a\[rq]:\[dq]\[dq] T}@T{ \[lq]a\[rq]:0 T}@T{ \[lq]a\[rq]:false T}@T{ \[lq]a\[rq]:null T} .TE .PP Coercing a non-number string to number outputs the \f[I]length\f[R] of the string. .PP Coercing a non-boolean string to boolean outputs \f[C]false\f[R] if the string is empty, \f[C]true\f[R] otherwise. .PP Type coercion only applies to \f[C]key=value\f[R] words, and individual words in a \f[C]-a\f[R] array. Coercing other words has no effect. .SH EXAMPLES .PP Create an object. Note how the incorrectly-formatted float value becomes a string: .IP .nf \f[C] $ jo tst=1457081292 lat=12.3456 cc=FR badfloat=3.14159.26 name=\[dq]JP Mens\[dq] nada= coffee\[at]T {\[dq]tst\[dq]:1457081292,\[dq]lat\[dq]:12.3456,\[dq]cc\[dq]:\[dq]FR\[dq],\[dq]badfloat\[dq]:\[dq]3.14159.26\[dq],\[dq]name\[dq]:\[dq]JP Mens\[dq],\[dq]nada\[dq]:null,\[dq]coffee\[dq]:true} \f[R] .fi .PP Pretty-print an array with a list of files in the current directory: .IP .nf \f[C] $ jo -p -a * [ \[dq]Makefile\[dq], \[dq]README.md\[dq], \[dq]jo.1\[dq], \[dq]jo.c\[dq], \[dq]jo.pandoc\[dq], \[dq]json.c\[dq], \[dq]json.h\[dq] ] \f[R] .fi .PP Create objects within objects; this works because if the first character of value is an open brace or a bracket we attempt to decode the remainder as JSON. Beware spaces in strings \&... .IP .nf \f[C] $ jo -p name=JP object=$(jo fruit=Orange hungry\[at]0 point=$(jo x=10 y=20 list=$(jo -a 1 2 3 4 5)) number=17) sunday\[at]0 { \[dq]name\[dq]: \[dq]JP\[dq], \[dq]object\[dq]: { \[dq]fruit\[dq]: \[dq]Orange\[dq], \[dq]hungry\[dq]: false, \[dq]point\[dq]: { \[dq]x\[dq]: 10, \[dq]y\[dq]: 20, \[dq]list\[dq]: [ 1, 2, 3, 4, 5 ] }, \[dq]number\[dq]: 17 }, \[dq]sunday\[dq]: false } \f[R] .fi .PP Booleans as strings or as boolean (pay particular attention to \f[I]switch\f[R]; the \f[C]-B\f[R] option disables the default detection of the \[lq]\f[C]true\f[R]\[rq], \[lq]\f[C]false\f[R]\[rq], and \[lq]\f[C]null\f[R]\[rq] strings): .IP .nf \f[C] $ jo switch=true morning\[at]0 {\[dq]switch\[dq]:true,\[dq]morning\[dq]:false} $ jo -B switch=true morning\[at]0 {\[dq]switch\[dq]:\[dq]true\[dq],\[dq]morning\[dq]:false} \f[R] .fi .PP Elements (objects and arrays) can be nested. The following example nests an array called \f[I]point\f[R] and an object named \f[I]geo\f[R]: .IP .nf \f[C] $ jo -p name=Jane point[]=1 point[]=2 geo[lat]=10 geo[lon]=20 { \[dq]name\[dq]: \[dq]Jane\[dq], \[dq]point\[dq]: [ 1, 2 ], \[dq]geo\[dq]: { \[dq]lat\[dq]: 10, \[dq]lon\[dq]: 20 } } \f[R] .fi .PP The same example, using object paths: .IP .nf \f[C] $ jo -p -d. name=Jane point[]=1 point[]=2 geo.lat=10 geo.lon=20 { \[dq]name\[dq]: \[dq]Jane\[dq], \[dq]point\[dq]: [ 1, 2 ], \[dq]geo\[dq]: { \[dq]lat\[dq]: 10, \[dq]lon\[dq]: 20 } } \f[R] .fi .PP Without \f[C]-d\f[R], a different object is generated: .IP .nf \f[C] $ jo -p name=Jane point[]=1 point[]=2 geo.lat=10 geo.lon=20 { \[dq]name\[dq]: \[dq]Jane\[dq], \[dq]point\[dq]: [ 1, 2 ], \[dq]geo.lat\[dq]: 10, \[dq]geo.lon\[dq]: 20 } \f[R] .fi .PP Create empty objects or arrays, intentionally or potentially: .IP .nf \f[C] $ jo < /dev/null {} $ MY_ARRAY=(a=1 b=2) $ jo -a \[dq]${MY_ARRAY[\[at]]}\[dq] < /dev/null [\[dq]a=1\[dq],\[dq]b=2\[dq]] \f[R] .fi .PP Type coercion: .IP .nf \f[C] $ jo -p -- -s a=true b=true -s c=123 d=123 -b e=\[dq]1\[dq] -b f=\[dq]true\[dq] -n g=\[dq]This is a test\[dq] -b h=\[dq]This is a test\[dq] { \[dq]a\[dq]: \[dq]true\[dq], \[dq]b\[dq]: true, \[dq]c\[dq]: \[dq]123\[dq], \[dq]d\[dq]: 123, \[dq]e\[dq]: true, \[dq]f\[dq]: true, \[dq]g\[dq]: 14, \[dq]h\[dq]: true } $ jo -a -- -s 123 -n \[dq]This is a test\[dq] -b C_Rocks 456 [\[dq]123\[dq],14,true,456] \f[R] .fi .PP Read element values from files: a value which starts with \f[C]\[at]\f[R] is read in plain whereas if it begins with a \f[C]%\f[R] it will be base64-encoded and if it starts with \f[C]:\f[R] the contents are interpreted as JSON: .IP .nf \f[C] $ jo program=jo authors=\[at]AUTHORS {\[dq]program\[dq]:\[dq]jo\[dq],\[dq]authors\[dq]:\[dq]Jan-Piet Mens \[dq]} $ jo filename=AUTHORS content=%AUTHORS {\[dq]filename\[dq]:\[dq]AUTHORS\[dq],\[dq]content\[dq]:\[dq]SmFuLVBpZXQgTWVucyA8anBtZW5zQGdtYWlsLmNvbT4K\[dq]} $ jo nested=:nested.json {\[dq]nested\[dq]:{\[dq]field1\[dq]:123,\[dq]field2\[dq]:\[dq]abc\[dq]}} \f[R] .fi .PP Read element values from a file in order to overcome ARG_MAX limits during object assignment: .IP .nf \f[C] $ ls | jo -a > child.json $ jo files:=child.json {\[dq]files\[dq]:[\[dq]AUTHORS\[dq],\[dq]COPYING\[dq],\[dq]ChangeLog\[dq] .... $ ls *.c | jo -a > source.json; ls *.h | jo -a > headers.json $ jo -a :source.json :headers.json [[\[dq]base64.c\[dq],\[dq]jo.c\[dq],\[dq]json.c\[dq]],[\[dq]base64.h\[dq],\[dq]json.h\[dq]]] \f[R] .fi .SH OPTIONS .PP \f[I]jo\f[R] understands the following global options. .TP -a Interpret the list of \f[I]words\f[R] as array values and produce an array instead of an object. .TP -B By default \f[I]jo\f[R] interprets the strings \[lq]\f[C]true\f[R]\[rq] and \[lq]\f[C]false\f[R]\[rq] as boolean elements \f[C]true\f[R] and \f[C]false\f[R] respectively, and \[lq]\f[C]null\f[R]\[rq] as \f[C]null\f[R]. Disable with this option. .TP -e Ignore empty stdin (i.e.\ don\[cq]t produce a diagnostic error when \f[I]stdin\f[R] is empty) .TP -p Pretty-print the JSON string on output instead of the terse one-line output it prints by default. .TP -v Show version and exit. .TP -V Show version as a JSON object and exit. .SH BUGS .PP Probably. .PP If a value given to \f[I]jo\f[R] expands to empty in the shell, then \f[I]jo\f[R] produces a \f[C]null\f[R] in object mode, and might appear to hang in array mode; it is not hanging, rather it\[cq]s reading \f[I]stdin\f[R]. This is not a bug. .PP Numeric values are converted to numbers which can produce undesired results. If you quote a numeric value, \f[I]jo\f[R] will make it a string. Compare the following: .IP .nf \f[C] $ jo a=1.0 {\[dq]a\[dq]:1} $ jo a=\[rs]\[dq]1.0\[rs]\[dq] {\[dq]a\[dq]:\[dq]1.0\[dq]} \f[R] .fi .PP Omitting a closing bracket on a nested element causes a diagnostic message to print, but the output contains garbage anyway. This was designed thusly. .SH RETURN CODES .PP \f[I]jo\f[R] exits with a code 0 on success and non-zero on failure after indicating what caused the failure. .SH AVAILABILITY .PP .SH CREDITS .IP \[bu] 2 This program uses \f[C]json.[ch]\f[R], by Joseph A. Adams. .SH SEE ALSO .IP \[bu] 2 .IP \[bu] 2 .IP \[bu] 2 .IP \[bu] 2 strtod(3) .SH AUTHOR .PP Jan-Piet Mens