* "Bringing the Unix philosophy to the 21st century (2019)" (https://blog.kellybrazil.com/2019/11/26/bringing-the-unix-ph...) - https://news.ycombinator.com/item?id=28266193 238 points | Aug 22, 2021 | 146 comments
* "Tips on adding JSON output to your CLI app" (https://blog.kellybrazil.com/2021/12/03/tips-on-adding-json-...) - https://news.ycombinator.com/item?id=29435786 183 points | 11 months ago | 110 comments
https://unix.stackexchange.com/questions/197809/propose-addi...
Just like we have stdout and stderr, header lines such as those produced by `ps` should be printed to stdmeta. Curl is the worse offender here, outputing meta lines to stderr instead of stdout. A stdmeta file descriptor would make it clear what is data, what is an error, and what is _describing_ the data.
A ringbuffer filetype. Similar to a named pipe file (see: fifo(7)[^1]), but without consuming the contents on read and automatically rotating out the oldest lines.
Of course, there would be some complexities around handling read position as lines are being rotated out from under you.
Or pipe it into rq [2] to convert the format to yaml, toml etc.
[1]: https://stedolan.github.io/jq/tutorial/ [2]: https://github.com/dflemstr/rq#format-support-status
When wrestling with sed/awk in trying to parse results of a shell command, I've often thought that a shell-standard, structured outpout would be very handy. Powershell[0] has this, but it's a binary format - so not human-readable. I want something in the middle: human- and machine-readable. Without either having to do parsing gymnastics.
jc isn't quite that shell standard, but looks like it goes a long way towards it. And, of course, when JSON falls out of fashion and is replaced by <whatever>, `*c` can emerge to fill the gap. Nice.
Well, yes - powershell passes binary objects but as you can always:
1) access their properties 2) pass them downstream 3) serialize to json/csv 4) instantiate from json/csv
I think this is both human- and machine-readable enough (even through internal format is binary, but working with Powershell you are never really exposed to it).
How do you think it can be improved?
In my opinion object io IS the best part of powershell - it allows us to ditch results wrangling with sed/awk/grep entirely. I'm super interested if there's an even better way forward.
That seems unnecessary. Traditionally, shells have always used text streams. JSON is just text that follows a given convention. Couldn't what you are describing be implemented by setting environment variables or using command line flags?
For example:
PREFERRED_OUTPUT_FORMAT="JSON"
--output-format="JSON"
--input-format="JSON"
Tools that can generate and consume structured text formats are a good idea, but they should be flexible enough that they can even work with other tools that have not been written yet.
"This is the Unix philosophy: Write programs that do one thing and do it well. Write programs to work together. Write programs to handle text streams, because that is a universal interface." --Doug McIlroy
HTTPS://GitHub.com/lmorg/murex
Why does this site recommend using "paru", "aura" or "yay" to install it on Arch? I have been using Arch for a decade or so but have never even heard of such tools. They don't even have pages in the Arch wiki, and only yay ("Pacman wrapper and AUR helper written in go") is available via the standard repository.
Begs the question: what is so wrong with plain pacman?
EDIT: Okay so seems they were previously on AUR and once accepted to community repository they just forgot to stop recommending an AUR wrapper for installing: https://github.com/kellyjonbrazil/jc/commit/f2dd7b8815edc92e...
EDIT2: Created a PR with the GitHub.dev editor .. Absolutely blown away by how easy it was! Feels like the future of development.. https://github.com/kellyjonbrazil/jc/pull/310
But this is 100% going in my toolbox - I can think of a couple of scripts that I can update to use this right of the bat!
I am the author of SPyQL [1]. Combining JC with SPyQL you can easily query the json output and run python commands on top of it from the command-line :-) You can do aggregations and so forth in a much simpler and intuitive way than with jq.
I just wrote a blogpost [2] that illustrates it. It is more focused on CSV, but the commands would be the same if you were working with JSON.
[1] https://github.com/dcmoura/spyql [2] https://danielcmoura.com/blog/2022/spyql-cell-towers/
That said, I would argue that JSONLines is a better universal output format when you're dealing with pipelines. If the output is one giant JSON array, then you have to wait for a long-running program to finish completely before the output can be passed on to the next long-running program. If you output one JSON line at a time as you process your input, then the next program in the pipeline can get started on processing already without waiting for the first to finish completely.
Discussion: https://news.ycombinator.com/item?id=25006277 366 points | Nov 6, 2020 | 91 comments
Blog post with examples here: https://blog.kellybrazil.com/2020/08/30/parsing-command-outp...
{"filename":"drwxr-xr-x 16 root root 4096 Oct 4 11:21 ."}
instead of {"filename":".","flags":"drwxr-xr-x","links":16,"owner":"root","group":"root","size":4096,"date":"Oct 4 11:21"}
with LANG=US. This makes it really hard to trust such a tool.It's not unheard of for tools to require `C` locale for proper parsing:
$ LC_ALL=C ls -l | jc --ls
This is one of many inherent issues with using unstructured text as an API. That's why I believe there should be a JSON (or at least some other widely used format[1]) option for tools that have output that would be useful in scripts.[0] https://github.com/kellyjonbrazil/jc#locale
[1] formats should have good library support across many languages and nice filter/query capabilities from the command-line
Any custom parser of ls output would potentially have the same problem. Of course, it can be improved though – for example by looking at LANG – and it would be nice for such improvements to get into `jc`, so that other tools can rely on it at least more than doing the parsing directly themselves.
If anything, though, that's a good reason for a tool like this to exist rather than have every script that depends on these tools use their own, often hacky, parsing of the output.
[1] "WARNING: apt does not have a stable CLI interface. Use with caution in scripts."
dig +yaml google.com - It has to parse output of commands which may or may not be intended to be parsed and may or may not have a predictable format. The only way to overcome this is if this program becomes one of the Big Four "UN*X command output -> data" converters
- It casts things to "float/int"
- Depending on who made this library, the output itself may not be strict / predictable. Perhaps it will output JSON with two different key names in two different scenarios.
And don't forget that any of these issues will still come up even if they are accommodated for, due to versions of programs changing without the author of this tool knowing.But yeah basic things having intrinsic shortcomings is a given, when you're using UN*X.
From the nested article:
> Had JSON been around when I was born in the 1970’s Ken Thompson and Dennis Ritchie may very well have embraced it as a recommended output format to help programs “do one thing well” in a pipeline.
They had S-expressions and plenty more options. They also could have just made a format as you can tell with thousands of ad-hoc trendy new formats like YAML and TOML being spewed out every recent year now that programmers discovered data structures.
The casting to int/float is not done unless the underlying values are predictably documented to be numbers. There are rare cases where auto int/float conversions are done, but:
1) This is always documented, and
2) You can turn this functionality off via the —raw flag
Also, predictable schemas are documented with each parser. (e.g `jc -h —-arp`)
Even when using jq (written in C) my quick tests show that parsing json is really slow compared to parsing with simple unix tools like awk. I suspect that to come from the fact that the parser has to check the full json output first in order to print a result, while awk does not care about syntax of the output.
I compared two shell scripts both printing the ifindex of some network device (that is the integer in the first column) 10 times.
Using awk and head combined gives me 0,068s total time measured with the time command.
Using ip with the -j flag together with jq gives 0,411s.
Therefore the awk approach is 6 times faster. And here I used a binary (ip) that already supports json output and doesn't even need the mentioned jc.
While this whole test setup is somewhat arbitrary I experienced similar results in the past when writing shell scripts for, e.g., my panel. Reach out to me if you are interested in my test setup.
dig example.com | jc --dig
Seems a bit redundant. Maybe it should be the other way round? jc dig example.com
Similar to how you do time dig example.com $ jc dig example.com | jq -r '.[].answer[].data'
93.184.216.34
It uses the first argument to infer the command output type.`jc` doesn't need to know anything about the command producing the output - just the format of the output. So using a pipe and stdin makes a lot of sense.
I can imagine `jc` having some detection built in, from which it determines the command/content it's being parsed. Doesn't seem to have it, yet, and I'm generally no big fan of "magic" like this, but it would remove the redundancy.
Having it as a pipe, allows for much more, though.
some_expensive_command > out.log
jc --expensive-cmd < out.log
Or hourly_dig.sh > example_com_records_$(date +%F+%s)
cat example_com_records_* | jc --digI did implement auto-detection for `/proc` file parsers so you can just do:
$ cat /proc/foo | jc --proc
or $ jc /proc/foo
But you can specify each procfile parser directly if you want to as well.The power of plain text pipes is that you do not interpret them and that makes them fast, that is usefull because you handle both 100 bytes, 1MB and 1TB as input. You choose what you parse keeping it simple, fast and usually error free. This tool miss the, fast, simple and human readable part of debugging pipes. Which is fine!
minor updates to command-line tools can and do subtly alter the textual output of the tool, and the outputs of these tools are not standardized.
This is a step towards "objects passing messages" as originally conceived by Alan Kay, if my incomplete understanding of what he's said is correct, and that's a good thing, I think. Objects passing messages around is a very solid model for computing, to me. Note that I am stupid and don't understand much, if I'm honest.
I'd like to use it on embedded systems, where python is too large to fit. this tool can be widely deployed just like awk|sed|etc but it has to be in C for that.
https://github.com/kellyjonbrazil/jc/releases
This is still python under the hood and not as small of a binary as I would like, but it does work.
But yeah, for these types of utilities, relying on an external language runtime like Python/Node is pretty rough.
It is obvious that CLI commands should produce machine-readable output because they are often used in scripts, and accept machine-readable input as well. Using arbitrary text output was a mistake because it is difficult to parse, especially when spaces and non-ASCII characters are present.
A good choice would be a format that is easily parsed by programs but still readable by the user. JSON is a bad choice here because it is hard to read.
In my opinion, something formatted with pipes, quotes and spaces would be better:
eth0:
ip: 127.15.34.23
flags: BROADCAST|UNICAST
mtu: 1500
name: """"Gigabit" by Network Interfaces Inc."""
Note that the format I have proposed here is machine-readable, somewhat human-readable and somewhat parseable by line-oriented tools like grep. Therefore there might be no need for switches to choose output format. It is also relatively easy to produce without any libraries.Regarding idea to output data in /proc or /sys in JSON format, I think this is wrong as well. This would mean that reading data about multiple processes would require lot of formatting and parsing JSON. Instead or parsing /proc and /sys directly, applications should use libraries distributed with kernel, and reading the data directly should be discouraged. Because currently /proc and /sys are just a kind of undocumented API.
Also, I wanted to note that I dislike jq utility. Instead of using JSONPath it uses some proprietary query format that I constantly fail to remember.
- Pipe into gron (https://github.com/tomnomnom/gron) to get a `foo.bar.baz = val` kind of syntax.
- Pipe into visidata (https://www.visidata.org/) to get a spreadsheet-like editable view.
For example:
% jc -y date
---
year: 2022
month: Nov
month_num: 11
day: 3
weekday: Thu
weekday_num: 4
hour: 9
hour_24: 9
minute: 0
second: 22
period: AM
timezone: PDT
utc_offset:
day_of_year: 307
week_of_year: 44
iso: '2022-11-03T09:00:22'
epoch: 1667491222
epoch_utc:
timezone_aware: falseJust pipe it into a JSON-to-YAML script like this:
#! /usr/bin/python3
from ruamel import yaml
import json, sys, io
print(yaml.dump(json.load(sys.stdin))) jc dig example.com | jq
[
{
"id": 30081,
"opcode": "QUERY",
"status": "NOERROR",
"flags": [
"qr",
"rd",
"ra"
],
"query_num": 1,
"answer_num": 1,
"authority_num": 0,
"additional_num": 1,
"opt_pseudosection": {
"edns": {
"version": 0,
"flags": [],
"udp": 4096
}
},
"question": {
"name": "example.com.",
"class": "IN",
"type": "A"
},
"answer": [
{
"name": "example.com.",
"class": "IN",
"type": "A",
"ttl": 56151,
"data": "93.184.216.34"
}
],
"query_time": 0,
"server": "192.168.1.254#53(192.168.1.254)",
"when": "Thu Nov 03 14:06:40 CET 2022",
"rcvd": 56,
"when_epoch": 1667480800,
"when_epoch_utc": null
}
]
Rather readable to my mind. And you can rather easily transform it to your preferred human readable output format I guess.I think the powershell approach is a good one here too: powershell commands output binary streams of objects rather than text and it is powershell itself that has several standard ways of human readable outputs, most of which are automatic (but easily tweaked with an extra pipe or two). Standard human readable forms are nice, and even standardized there's no need to rely on parsing them back out into objects because they are already passed as objects so they can focus a bit more on "pretty" over "parse-able" (such as including human useful things like ellisions `…` on long columns).
https://en.wikipedia.org/wiki/PowerShell#Pipeline
>As with Unix pipelines, PowerShell pipelines can construct complex commands, using the | operator to connect stages. However, the PowerShell pipeline differs from Unix pipelines in that stages execute within the PowerShell runtime rather than as a set of processes coordinated by the operating system. Additionally, structured .NET objects, rather than byte streams, are passed from one stage to the next. Using objects and executing stages within the PowerShell runtime eliminates the need to serialize data structures, or to extract them by explicitly parsing text output. An object can also encapsulate certain functions that work on the contained data, which become available to the recipient command for use. For the last cmdlet in a pipeline, PowerShell automatically pipes its output object to the Out-Default cmdlet, which transforms the objects into a stream of format objects and then renders those to the screen.
How well would this format handle deeply nested structures? It seems like it would require a lot of space characters compared to nesting open and close characters: {} or () or []
How would escaping pipes, quotes, and spaces work to represent those character literals?
There are already numerous structured text formats: JSON, XML, S-expressions, YAML, TOML, EDN, and many more. Wouldn't this be yet another format? (https://xkcd.com/927/)
eth0 [
ip [127.15.34.23]
flags [[BROADCAST][UNICAST]]
mtu [1500]
name ["Gigabit" by Network Interfaces Inc.]
]
This is one of the things it was designed with in mind.It's even simpler and more flexible than S-expressions.
Handles deeply nested structures perfectly well. Has only 3 characters to escape (brackets and the escape character).
(I am the author)
At the same time it’s an epitome of everything that is wrong with current software landscape. Instead of fixing the deficiencies in upstream, once and for all, we just keep piling more and more layers on top.
Programs having structural data and APIs inside, that get translated into human representation - only to be re-parsed again into structural form. What could possibly go wrong?
If you are already in any programming environment, many of the tools already have better built in APIs. I mean who needs an “ls” or timestamp parser. Just use os.listdir or equivalent. As someone previously pointed out in this thread, the ls parser is in fact already broken, unsurprisingly. Mixing tools made for interactive use in automation is never a good idea.
The Unix philosophy sounds romantic in theory, but need structural data, throughout, to work reliably in practice. Kids, go with the underlying apis unless your tool has structured output.
If this were written in a performant language, if it simply aliased (i.e. invisibly) all common cli commands to a wrapper which would obviate the need for all of the text processing between steps in command pipelines, if it were versioned and I could include the version number in scripts, and finally if I could run versioned scripts through it to compile them into standard bash scripts (a big ask), I'd give it a 3 month test starting today. There'd be nothing to lose.
Just putting that out there for people who like to rewrite things in Rust. A slightly different version of this concept could allow for nearly friction-free adoption.
You also don’t like software written in C (the language jq is written in)?
The itch I can’t seem to scratch is how to run tasks in parallel and have logs that are legible to coworkers. We do JSON formatted logs in production and I’m wondering if something like this would help solve that set of problems.
Nushell that hit the front page earlier this week seemed to me to be limited by "compatible" apps, but wrapping all the big ones in a json converter superficially seems like a great solution to me.
I love it
I’ve been trying to build something like this but simply don’t have the free time currently.
The plan: adapt the parser VM from lpeg (or similar, there’s a paper I’ve been reading on an Earley parser VM) into a command line app that takes a grammar + text input (or stdin) and spits out json to a file (or stdout). Probably not as general purpose as this one but also wouldn’t need a pull request to add a new format.
All the pieces are there but without the free time…
I was actually curious if there was any demand for such a thing, I just want it to parse my payroll statements because this billion dollar company can only manage crappy pdfs and, well, it’s an interesting problem.
—edit—
Oh, output schema. Totally different than what I’m going on about.
Having true JSON Schema[0] is being considered, but on the back-burner due to the sheer number of parsers to build schemas for. Also, it is more difficult to accurately define the schema for a small subset of parsers since their command output are so variable.
I think json has several advantages though. It’s a relatively lightweight and widely known serialization standard, rich enough for most cases and extensible in others, and it has easy to use parsers in all major programming languages.
Also jsonlines is a simple addition that make it easy for json to play well with non-json aware older Unix tools.
It has a few shortcomings but I think its advantages outweigh them, and it’s become a pretty widely used standard in a short time.
In my perfect world (which, obviously doesn't exist), commands from tools "in the wild" are at least three letters long. With historical exceptions for gnutools: preferably they'd take the three-letter space, but two-letters (cd, ls, rm etc) is fine.
Two letter space outside of gnutools, is then reserved for my aliases. If jsonquery is too long to type, AND I lack autocomplete, then an alias is easy and fast to make. alias jq=jsonquery.
In the case of this tool, it will conflict with a specialised alias I have: `alias jc=javac -Werror`. Easy to solve by me with another alias, but a practical example of why I dislike tools "squatting" the two letter namespace.
E.g.:
alias jsonquery='command jq'It's something I appreciate about the powershell naming conventions. A lot of people mock the verbosity of the names of powershell commands and commandlets which require the "proper" name to be Verb-Noun qebab case monstrosities, but this was chosen for exactly the reasons of your pet peeve: short command names should be user aliases for work in a shell, and longer command names are great for avoiding namespace clashes in scripts and between users. The verbs and nouns create large (discoverable) namespaces.
For instance, this tool might be powershell named ConvertTo-ParsedJson. (ConvertTo-Json is an out of the box command that converts any powershell object to JSON.) It might suggest the user alias it by adding `Set-Alias -Name jc -Value ConvertTo-ParsedJson` but generally commands in powershell only offer such aliases as suggestions rather than defaults. (Though there are a lot of out of the box aliases for common powershell commands.)
It makes sense to me that powershell encourages long names first and allows and encourages users to have far more control over short aliases.