JC converts the output of popular command-line tools to JSON
github.com
github.com
$ ps --libxo=json | jq
{
"process-information": {
"process": [
{
"pid": "41389",
"terminal-name": "0 ",
"state": "Is",
"cpu-time": "0:00.01",
"command": "-bash (bash)"
},
[...]
It's not perfect though. ls had support, but it was removed for reasons[1]. It's not supported by all of the utilities, etc.This seems to be a great stop-gap with parsers for a LOT of different commands, but it relies on parsing text output that's not necessarily designed to be parsed. It would be nice if utilities coalesced around a common flag to emit structured output.
In PowerShell, structured output is the default and it seems to work very well. This is probably too far for Unix/Linux, but a standard "--json" flag would go a long way to getting the same benefits.
A better, structural way in which this could be fixed is to allow data structures to be exported in ELFs and have those data structures serialized into terminal output, which can then be outputted in the preferred format of the user, such as JSON, YAML, or processed accordingly.
I can't remember the exact utility--I think it was iostat--would use string interpolation to format output lines in JSON and combined with certains flags produced completely mangled output. Not sure if things have improved but I would have expected something like JSON lines when interval is provided.
Powershell and kubectl are miles ahead of libxo in useability imo
I like C a lot but one of the reasons I like Rust more these days is the ability to trivially implement complex serialization schemes without a ton of ad-hoc code and boilerplate.
OP has a blog post[0] which describes exactly this. `jc` is described as a tool to fill this role "in the meantime" -- my reading is that it's intended to serve as a stepping stone towards widespread `-j`/`--json` support across unix tools.
[0] https://blog.kellybrazil.com/2019/11/26/bringing-the-unix-ph...
Libxo happens to be in the base system, but it is generally available:
PowerShell goes a step beyond JSON, by supporting actual mutable objects. So instead of just passing through structured data, you effectively pass around opaque objects that allow you to go back to earlier pipeline stages, and invoke methods, if I understand correctly: https://learn.microsoft.com/en-us/powershell/module/microsof....
I'm rather fond of wrappers like jc and libxo, and experimental shells like https://www.nushell.sh/. These still focus on passing data, not objects with executable methods. On some level, I find this comfortable: Structured data still feels pretty Unix-like, if that makes sense? If I want actual objects, then it's probably time to fire up Python or Ruby.
Knowing when to switch from a shell script to a full-fledged programming language is important, even if your shell is basically awesome and has good programming features.
I do think objects make plenty of sense in languages like AppleScript, which essentially allowed users to script running GUI applications. And similarly, Powershell's objects might be right for Windows.
But nushell shows how far you can push "dumb" structured data. And it still feels "Unix-like", or at least "alternate universe Unix-like."
The other reason I'm suspicious of objects in shells is that shell pilelines are technically async coroutines operating over streams! That's already much further into the world of Haskell or async Rust than many people realize. And so allowing "downstream" portions of a pipeline to call back into "upstream" running programs and to randomly change things introduces all kinds of potential async bugs.
If you're going to have a async coroutines operating on streams, then having immutable data is often a good choice. Traditional Unix shells do exactly this. Nushell does it, too, but it replaces plain text with structured data.
It's true, you are indeed passing around full-blown .NET runtime objects. In fact your whole shell is running inside an instance of the .NET runtime, including the ability to dynamically load .NET DLL's and even directly invoke native API's.
It feels a bit like JS in the sense that you're best off sticking to "the good parts", where you get the power of structured input/output but you don't end up trying to, for example, implement high performance async code, even though you technically could.
In specific:
https://svnweb.freebsd.org/base?view=revision&revision=32810...
> libxo imposes a large burden on system utilities. In the case of ls, that burden is difficult to justify -- any language that can interact with json output can use readdir(3) and stat(2).
Which rather misses the point of being able to use JSON in shell scripts.
True, and yet it's extremely common to parse output in bash scripts and other automations, so in a sense it's just centralizing that effort. That being said at least when you do it yourself you can fix problems directly.
I don't know if NuShell has it, I haven't tried.
In any case, it's much better for tools to output more-parseable data in the first place. Whitespace-delimited columns are fine of course, but not so much when the data can contain whitespace, as in the output from `ps`.
I don't see much reason why JSONLines (https://jsonlines.org/) / NDJSON (https://ndjson.org/) can't be a standard output format from most tools, in addition to tables.
As for the reason of removal:
any language that can interact with json output can use readdir(3) and stat(2).
Ugh. Any language of course can do it. But that's basically telling users that they need to reimplement ls(1) themselves if they want to use any of its output and features in scripts.I understand if the maintenance burden is too high to put it in ls(1) itself, but it's a shame that no tool currently does this. The closest we have is a feature request in Eza: https://github.com/eza-community/eza/issues/472
In 20 years, assuming some semblance of moore’s law still holds for storage/RAM/gpu, I’m right there with you.
Is there a good fine-tuning workflow with ollama?
Solution: throw a high end GPU with 24GB RAM and a million dollar of training at it.
Yeah, great solution.
Also, an LLM trained to be good at this task has many more applications than just turning command output into structured data. It's actually one of the most compelling business use cases for LLMs
You have a small C program that processes this data in memory, and dumps it to stdout in tabular text format.
Rather than simplify by stripping out the problematic bit (the text output), you suggest adding a large, cutting-edge, hard to inspect and verify piece of technology that transforms that text through uncountable floating point operations back into differently-formatted UTF8.
It might even work consistently (without you ever having 100% confidence it won't hallucinate at precisely the wrong moment).
You can certainly see it being justified for one-off tasks that aren't worth automating.
But to shove such byzantine inefficiency and complexity into an engineered system (rather than just modify the original program to give the format you want) offends my engineering sensibilities.
Maybe I'm just getting old!
I’d challenge that. Try working with your upstream. It’s easier than ever nowadays to submit issues and PRs on GitHub.
Building layers upon layers, just work around minor issues in a tool is not wise.
Anyway, I do try, but in my experience, if it happens, it's not over night and your going to have to maintain a work around for an amount of time
Maybe the best middle ground is to have an LLM write the parser. Lowers the development cost and runtime performance, in theory
there are plenty of programmers who do not know how to write lexers, parsers, and grammars
As I said before, I maintain a project like this. I also happen to work for a company that specialises in the use of generative AI. So I’m well aware of the power of LLMs as well as the problems of this very specific domain. The ideas you’ve expressed here are, at best, optimistic.
by the time you’ve solved all the little quirks of ML you’ll have likely invested far more time on your LLM then you would have if you’d just written a simple parser and, ironically, needed someone far more specialised to write the LLM than your average developer.
This simply isn’t a problem that needs a LLM chucked at it.
You don’t even need to write lexers and grammars to parse 99% of application output. Again, I know this because I’ve written such software.
This is not a serious suggestion
Developers make mistakes too, so there are no guarantees either way. Each of your questions can be asked of handwritten code too
It's not a question of "is the output always correct". Nothing is so binary in the real world. A well hand-maintained solution will trend further towards correctness as bugs are caught, reported, fixed, regression tested, etc.
Conversely, you could parse an IP address by rolling 4d256 and praying. It, too, will sometimes be correct and sometimes be incorrect. Does that make it an equally valid solution?
¯\_(ツ)_/¯
> Be kind. Don't be snarky. Converse curiously; don't cross-examine. Edit out swipes.
> Comments should get more thoughtful and substantive, not less, as a topic gets more divisive.
> Eschew flamebait. Avoid generic tangents. Omit internet tropes.
What rule applies when the initial comment is not thoughtful and substantive?
The latest "hammer" is AI.
Lots of commenters here are suggesting to use a complex AI to solve simple text parsing. Maybe you can't see the problem with that, but it's like using 1000 Watts of power to solve something that should take 1 microwatt, just because "new, shiny" AI is here to save us all from having to parse some text.
I'm not making assumptions about what people are commenting about in this thread. Your comment comes off like a subtle troll.
Most of the commands are pretty old and do not change anymore. Many parsers are not even commands but standard filetypes (YAML, CSV, XML, INI, X509 certs, JWT, etc.) and string types (IP addresses, URLs, email addresses, datetimes, etc.) which don't change or use standard libraries to parse.
Additionally, I get a lot of support from the community. Many new parsers are written and maintained by others, which spreads the load and accelerates development.
<rant>
Since I started programming in the 80s, I've noticed a trend where most software has adopted the Unix philosophy of "write programs that do one thing and do it well". Which is cool and everything, but has created an open source ecosystem of rugged individualism where the proceeds to the winners so vastly exceeds the crumbs left over for workers that there is no ecosystem to speak of, just exploitation. Reflected now in the wider economy.
But curated open source solutions like jc approach problems at the systems level so that the contributions of an individual become available to society in a way that might be called "getting real work done". Because they prevent that unnecessary effort being repeated 1000 or 1 million times by others. Which feels alien in our current task-focussed reality where most developers never really escape maintenance minutia.
So I'm all in favor of this inversion from "me" to "we". I also feel that open source is the tech analog of socialism. We just have it exactly backwards right now, that everyone has the freedom to contribute, but only a select few reap the rewards of those contributions.
We can imagine what a better system might look like, as it would start with UBI. And we can start to think about delivering software resources by rail instead of the labor of countless individual developer "truck drivers". Some low-hanging fruit might be: maybe we need distributions that provide everything and the kitchen sink, then we run our software and a compiler strips out the unused code, rather than relying on luck to decide what we need before we've started like with Arch or Nix. We could explore demand-side economics, where humans no longer install software, but dependencies are met internally on the fly, so no more include paths or headers to babysit (how early programming languages worked before C++ imposed headers onto us). We could use declarative programming more instead of brittle imperative (hard coded) techniques. We could filter data through stateless self-contained code modules communicating via FIFO streams like Unix executables. We could use more #nocode approaches borrowed from FileMaker, MS Access and Airtable (or something like it). We could write software from least-privileges, asking the user for permission to access files or the networks outside the module's memory space, and then curate known-good permissions policies instead of reinventing the wheel for every single program. We could (will) write test-driven software where we design the spec as a series of tests and then AI writes the business logic until all of the tests pass.
There's a lot here to unpack here and a wealth of experience available from older developers. But I sympathize with the cognitive dissonance, as that's how I feel every single day witnessing the frantic yak shaving of "modern" programming while having to suppress my desire to use these other proven techniques. Because there's simply no time to do so under the current status quo where FAANG has quite literally all of the trillions of dollars as the winners, so decides best practices while the open source community subsists on scraps in their parent's basement hoping to make rent someday.
If you read further down in the documentation, you can just prefix your command with `jc` (e.g. `jc ls`). The `--cmd` param is actually a good idea, since it allows you to mangle the data before converting it (e.g. you want to grep a list before converting it).
Regarding maintenance, most of the basic unix commands' output shouldn't change too much (they'd be breaking not only this tool but a lot of scripts). I wouldn't expect it to break as often as you imagine, at least not because of other binaries being updated.
However, unstructured files, or files that all have their own formats, is also equally hampering. Trying to even parse an nginx log file can be annoying with just awk or some such.
One of the big disadvantages is that large system rewrites and design changes cannot be executed in the linux userland.
All to say, I'd love a smarter shell, I love files, I have my awk book sitting next to me, but I think it's high time to get some serious improvements on parsing data.
In the same way programs are smart enough to know to render colored output or not, I'd love it if it could dump structured output (or not)
Though I guess the real endgame here is for upstream tools to eventually recognize the value and learn how to directly supply structured output.
journalctl -o json
And applications using journald directly can provide their own custom fields.The even lower hanging fruit is to implement json output as a command line argument in all cli tools. I would love to see this done for the gnu core utils.
And it's like pulling teeth to get any improvement, because the moment somebody like Lennart tries to get rid of decades of old cruft, drama erupts.
And even JSON is still not quite there. JSON is an okay-ish idea, but to do this properly what we need is a format that can expose things like datatypes. More like PowerShell. So that we can do amazing feats like treating a number like a number, and calculating differences between dates by doing $a - $b.
I totally love it when liars frontmanning the project say it's a project and an init system and a system layer that will replace everything. But it won't! Pay no attention to the inconsistent messaging, the "gentle pushes" to get other distros to use it, etc. Also, let's not mention he basically hoodwinked the entire community by switching teams to Microsoft. And people like you eat his work up! Are you sure you like free software?
So does everything else, because of consumer drives. Fun fact: filesystems by default only guarantee the integrity of the filesystem's structure itself. The promise is that after power loss, the basic structures of the filesystem won't be corrupt, but makes no big claims of reliability about the data written. Blocks of random junk in your data, blocks of NULLs, even parts of other files (maybe deleted data) are all things I've seen happen.
Databases and the like take serious effort to ensure safety, but that greatly slows down performance. So it's kind of a hard sell for a log system that's not supposed to be a performance impact.
For this contingency, you can do log shipping, but in general, system logs shouldn't be expected to be reliable in the face of a crash, since to my knowledge all normal daemons (including rsyslogd) buffer data before flushing to disk.
> You can forget just mounting a drive and checking its journald logs -- your machine has to be started by systemd in order to read journald logs!
No, it doesn't. There are file arguments to journalctl. Read the manpage, sheesh.
> I totally love it when liars frontmanning the project say it's a project and an init system and a system layer that will replace everything. But it won't! Pay no attention to the inconsistent messaging, the "gentle pushes" to get other distros to use it, etc.
Meh. Paranoia.
> Also, let's not mention he basically hoodwinked the entire community by switching teams to Microsoft. And people like you eat his work up! Are you sure you like free software?
Free Software is a licensing/distribution concept completely unrelated to whether one likes or not Microsoft's technical decisions. Some I really hate, and some are actually pretty cool.
I'm for Free Software because I like the licensing philosophy, not because I believe Unix is the best thing since sliced bread. In fact I believe Unix started as a bunch of good ideas but that have not kept up and so needs a bit of work to remain a good system to use.
... And how are you going to produce the journal file to read? ... with systemd. If you broke the system and it won't boot, you'll need to boot from systemd and check with journalctl because the journal can't be accessed otherwise. That usually requires a liveUSB running systemd to pull off. This is why you don't use binary logs.
Compared to `less /var/log/messages` from any Linux, I know which one I'm trusting.
I like controlling my system instead of having it controlled for me, thanks.
What do you mean "produce"? They were produced while it was running, they can be found in /var/log/journal
> If you broke the system and it won't boot, you'll need to boot from systemd and check with journalctl because the journal can't be accessed otherwise.
Obviously? I'm not seeing the problem. It's not like you're getting anything from that system without having a way to mount your XFS/Ext4/BTRFS/LVM/luks/whatever setup. You need to boot a distro compatible with that to do it.
So of course you have to boot a Linux distro, which will easily have all the tooling available, including to deal with the journald stuff.
It's just a complete non-problem.
> I like controlling my system instead of having it controlled for me, thanks.
I'm not sure what that means exactly.
A sane system doesn't need a whole lot just to check logs. LVM and LUKS are different due to cryptographic needs. systemd meanwhile has little reason to store logs in binary format. The promises that are alleged are not concerns to anyone except enterprise.
I just don't buy the problem as legitimate. It's an aesthetic problem, not a real problem. Sysadmins clearly have no problem with the fact that XFS is not a human readable format, or I don't recall anybody making a stink about using Berkeley DB for a whole bunch of stuff.
> systemd meanwhile has little reason to store logs in binary format.
Quite a few actually. Indexing, transparent compression, clear storage of arbitrary amounts of data with well delimited fields, quick seeking. Makes for a compact and very well performing system.
You can't quickly seek a .gz text file, while journald will tell you what happened a week ago at 3 AM in a few ms.
> The promises that are alleged are not concerns to anyone except enterprise.
Or people who realize there's a bit more to logs than 'tail' and 'grep'.
Eg, journald trivially will give you a log from a given timeframe that interleaves the logs of a proxy, httpd, database and application server, actually producing a log in which a request can be logically followed through the different services it went through, with timestamps in microseconds.
In an application that's designed for it, you can actually ask for logs regarding to a given host, user, etc.
If you've ever done log parsing, well, now you don't need to ever write a regex to split a .log by fields, because that was already done for you, and you can have UNIX timestamps directly instead of doing date parsing.
There is always ConvertFrom-String. The problem is that it is one of the least intuitive and worst-performing commands I've used in Powershell. It's awful and I hate it. It's like writing sed and awk commands without the benefit of sed and awk's maturity and ubiquity. IMX, only Compare-Object has been worse.
It's pretty much what powershell did, though.
Updating coreutils is a losing game at this point. Of course people will get angry when a well designed solution gradually takes over simply, because it is better.
That's a key problem with the developer story on Windows, especially coming from GNU/Linux. Where are my standard compilers and libraries? What use do I have for a terminal on Windows? In bash, I get powerful commands and a simple text-based pipeline hooked up to coreutils and literally anything else that runs on the command line, which is a metric ton of software in that ecosystem.
Back to Windows. What would I want PowerShell to do? What would I use PowerShell for that I wouldn't want to just use Bash instead? Windows doesn't have coreutils or nice command line software to do fun or powerful things with.
I see PowerShell as a "look we have a terminal!" from Windows, but nothing that I want to do in said terminal to motivate me to learn.
Sounds like you've never actually used windows for real tech stuff...
However this is social media, not an academic paper, and you didn't put much effort into backing your claims, either.
I actually wish there was just a third standard output for machine readable content, which your terminal doesn’t print by default. When you pipe, this output is what gets piped (unless you redirect), it’s expected to be jsonl, and the man page is expected to specify a contract. Then stdout can be for humans, and while you can parse it, you know what you’re doing is fragile.
Of course, that’s totally backwards incompatible, and as long as we’re unrealistically reinventing CLIs from the foundations to modernize them, I have a long list of changes I’d make.
And this is a really big issue that threatens to derail the whole project. If my script runs a pipeline `a | b | c` and utility b gets updated with a breaking change to the schema, it breaks the entire script. Now I've got to go deep into the weeds to figure out what happened, and the breakage might not be visible in the human-readable output. So to debug I'll have to pass the flag to each tool to get it to print all the json to stdout, and then sit there eyeballing the json to figure out how the schema changed and what I need to do to fix my script.
Seems like a big mess to me. Unless there's something I'm missing?
That is, if I have a CLI program that spits out a list of IP addresses and one day I want to also output the corresponding dns names, I can simply add the "dns" field and existing pipelines will ignore the field and work just fine.
This is better than grep/awking/etc. unstructured text to STDOUT because, depending on how the author decides to add the new field, it can easily break existing pipelines that rely on the shape of the data to stay the same.
I'm not sure how that's a particular burden. If you have `a | b | c` and you want to debug the output of b, you already have to pull that out and debug it or `tee` it, because its stdout is being piped otherwise. This would be the same, except that you'd pipe (or tee) "machine out" to stdout.
Or, since we're making wild backwards incompatible changes anyway, add a directive to the shell that makes it dump all "machine out" to stdout, a la "set +x". Now you don't even have to change your code. Just wrap the line in `set -o dump-machine-out` and `set +o dump-machine-out`
Except for that last jsonl part, various commands already do something like this with stdout by detecting what's on the other end.
https://unix.stackexchange.com/questions/515778/how-does-a-p...
And then if the user wants to debug their script, they need to know to pipe your command to `cat` or whatever to see what's actually getting passed through.
You probably already know this, but for those who do not, you can configure nginx to generate JSON log output.
Quite handy if you are aggregating structured logs across your stack.
But, unfortunately, sensible is doing some heavy lifting here and reality is... well, reality. While the output of things like the LSI/Broadcom StorCLI 'suffix the command with J' approach and some of PowerShell's COM-hiding wrappers (which are depressingly common) is technically JSON, the end result is so mindbogglingly complex-slash-useless, that you're quickly forced to revert to 'OK, just run some regexes on the plain-text output' kludges anyway.
Having said that, I'll definitely check this out. If the first example given, parsing dig output, is indeed representative of what this can reliably do, it should be interesting...
`aws s3 ls | jc —-aws=1.2.3`
What a nightmare.
jc 'aws sts get-caller-identity' | jq [..]
That way the aws process can be a subprocess of jc, which can read where the binary is and get its version automatically.
I'd expect this to not be a huge problem in practice because this is mostly for those well established unix cli tools of which the output has mostly ossified anyway. Many modern and frequently updated tools support native JSON output.
The s3 sub command annoyingly doesn’t. Which I’m guessing is the reason the GP used that specifically.
It may even be useful to add that information to this repo.
As does "lldpctl"
Ansible provides details about systems in JSON called 'facts'. The intention is to use these to inform automation
I know why it worked that way in Research Unix for the PDP-11. It's a property of the hokey trick used to make fork(II) work on tiny machines. It didn't have to stay that way for four decades.
Too many "lets fix the command line" (nushell, pwsh) have noble goals, but also start with "first let's boil the ocean".
We need to easily ingest old shitty text output for a little while to move to the new world of structured IO.
Fedora Toolbox has been wonderful for this exact use case (installing Python tools), but for utilities like this that will be part of a bash pipe chain for me, toolbox won't cut it.
PIPX_HOME=/usr/local/pipx PIPX_BIN_DIR=/usr/local/bin pipx install app==1.2.3
It sets up an isolated install for each app with only its deps and makes it transparent.The distro installation tree of Python is for the exclusive use of your distro because core apps cloud-init, dnf, firewalld are built against those versions.
For others: https://github.com/pypa/pipx
It's also in the Fedora repos: dnf install -y pipx
{
"lines": [
"line1 bla bla",
"line1 bla bla",
],
"words": [
"word1",
"word2",
],
}
With enough representations (maybe multiple of the same thing) to make it easy to process, without knowing sed or similar. It seems hacky but it would not require any maintenance for each command, and would only change if the actual output changes.Not having to know sed or a similar tool. Most unix tools are structured in lines, columns, etc. anyway.
But you get a JSON with a list of lines that you still have to process in some way. Instead of having a program that reads the input line by line, you read a JSON that contains a list of lines.
It's a messy, hairy, awful language. Consistently inconsistent, dynamically-typed in the worst ways, "two googles per line" complexity, etc.
But for the convenience of being able to combine shell-like access to various tools and platforms combined with the "everything is a stream of objects" model, it can't be beat in my experience.
And you can still do all the bash-like things for tools that don't have good Powershell wrappers that will convert their text-streams into objects. Which, sadly, is just about everything.
What are you building with it that would be harder in bash or another shell? I'm not seeing the value of passing around opaque objects instead of text.
Jason/XML/csv files and API results all get turned into PS objects easily, as do things with config objects like file permissions or SQL servers or whatnot. There's a bunch of dumb ideas like non-file filesystems for things like SQL and the Windows Registry, but the basic concept of "call command-line tools and navigate the filesystem with the ease of Bash, but also work with objects like Python or JS" works well for me.
Disclaimer: I'm the author of `jc`.
$ dig example.com | txr dig.txr
[{"query_time":"1","rcvd":"56","answer_num":1,"status":"NOERROR",
"when_epoch":1702030676,"opcode":"QUERY","udp":"65494","opt_pseudosection":{"edns":{"udp":65494,"flags":[],"version":"0"}},
"query_num":1,"question":{"name":"example.com.","type":"A","class":"IN"},
"server":"127.0.0.53#53(127.0.0.53)","id":"48295","authority_num":0,
"answer":[{"name":"example.com.","type":"A","data":"93.184.216.34","ttl":"4441",
"class":"IN"}],
"additional_num":1,"when":"Fri Dec 08 10:17:56 PST 2023"}]
$ cat dig.txr
@(bind sep @#/[\s\t]+/)
@(skip)
;; ->>HEADER<<- opcode: @opcode, status: @status, id: @id
;; flags: qr rd ra; QUERY: @query, ANSWER: @answer, AUTHORITY: @auth, ADDITIONAL: @additional
@(skip)
;; OPT PSEUDOSECTION:
; EDNS: version: @edns_ver, flags:@flags; udp: @udp
@(skip)
;; QUESTION SECTION:
;@qname@sep@qclass@sep@qtype
@(skip)
;; ANSWER SECTION:
@aname@sep@ttl@sep@aclass@sep@atype@sep@data
;; Query time: @qtime msec
;; SERVER: @server
;; WHEN: @when
;; MSG SIZE rcvd: @rcvd
@(do (put-jsonl #J^[{
"id" : ~id,
"opcode" : ~opcode,
"status" : ~status,
"udp" : ~udp,
"query_num" : ~(tofloat query),
"answer_num" : ~(tofloat answer),
"authority_num" : ~(tofloat auth),
"additional_num" : ~(tofloat additional),
"opt_pseudosection" :
{
"edns" :
{
"version" : ~edns_ver,
"flags" : [],
"udp" : ~(tofloat udp)
}
},
"question" :
{
"name" : ~qname,
"class" : ~qclass,
"type" : ~qtype
},
"answer" :
[
{
"name" : ~aname,
"class" : ~aclass,
"type" : ~atype,
"ttl" : ~ttl,
"data" : ~data
}
],
"query_time" : ~qtime,
"server" : ~server,
"rcvd" : ~rcvd,
"when" : ~when,
"when_epoch" : ~(time-parse "%a %b %d %T %Z %Y" when).(time-utc)
}]))
The latest TXR (292 as of time of writing) allows integers in JSON data, so (toint query) could be used.I wonder how well this could work/interact with Powershell.