179 karma · joined September 7, 2016
I wonder if Deno can be used with https://quarto.org/ now?
It's a good idea for a project like this. One bit of feedback is that it would be helpful to have a bit more context for the images - the titles get elipsised on mobile and when viewing a full image you can't see the title.
My immediate use case is very IO-bound, and won't use huge message sizes, so decoding/encoding performance probably isn't a huge problem. My hunch is It should be fast enough for event handling small messages, or it'd also be fine for passing binary buffers between Python and JS. (E.g using an array library like numpy and shipping an array over as a buffer, with some other JS objects for extra metadata.) (More so if I implemented reading/writing an mmap.)
https://docs.python.org/3/library/types.html#types.MappingPr...
It is, but if you already have bash, adding another shell script isn't much of a jump. e.g. I'd feel OK about committing jb to another repo for use from a .envrc file to set up an environment, whereas committing a binary would not feel good.
> Biggest crime of the Unix world probably.
Sorry if I'm perpetuating this! :) My take is that problem is not with bash, the problem is that it's hard for more advanced tools to replace it.
jb doesn't have high-level knowledge of other formats, it can read from common shell data sources, like command-line arguments, environment variables and files. It gives you ways to pull several of these sources into a single JSON object.
jb understands some simple/general formats commonly used in shell environments:
- key=value pairs (e.g. environment variable declarations, like the `env` program prints
- delimited lists, like a,b,c,d; (but any character can be the delimiter) including null-delimited (commonly used to separate lists of file paths)
- JSON itself — jb can validate and merge together arrays and objects
You can use these simple sources to build up a more complex structure, e.g. using a pipeline of other command line tools to generate null-delimited data, or envar declarations, then consuming the program's output via process substitution <(...). (See the section in the README that explains process substitution if you're not familiar, it's really powerful.)
So jb is more suited to creating ad-hoc JSON for specific tasks. If you had both jc and jb available and jc could read the source you need, you'd prefer jc.
For good measure, this is how you might do the same with jb:
$ jb Hello=world array:number[]@<(seq 10) object:json@<(date=$(date -Iseconds) jb @date)
{"Hello":"world","array":[1,2,3,4,5,6,7,8,9,10],"object":{"date":"2024-07-03T19:26:36+00:00"}}
Alternatively, using the :{} object entry syntax: jb Hello=world array:number[]@<(seq 10) object:{}=date=$(date -Iseconds)
{"Hello":"world","array":[1,2,3,4,5,6,7,8,9,10],"object":{"date":"2024-07-03T19:30:26+00:00"}}Second is situations where you'd rather not add an additional dependency, but bash is pretty much a given. For example, CI environments, scripts in dev environments, container entrypoints. Or things that area already written in bash.
I don't advocate writing massive programs in bash, for sure it's better to turn to a proper language before things get hairy. But bash is just really ubiquitous, and most people who do any UNIX work will be able to deal with a bit of shell script.
$ jb size:number=oops; echo $?
json.encode_number(): not all inputs are numbers: 'oops'
json(): Could not encode the value of argument 'size:number=oops' as a 'number' value. Read from inline value.
␘
1
If you pipe the jb error into jq, jq fails to parse the JSON (because of the Cancel ctrl char) and also errors: $ jb size:number=oops | jq
json.encode_number(): not all inputs are numbers: 'oops'
json(): Could not encode the value of argument 'size:number=oops' as a 'number' value. Read from inline value.
parse error: Invalid numeric literal at line 2, column 0
$ declare -p PIPESTATUS
declare -a PIPESTATUS=([0]="1" [1]="4")
So jq exits with status 4 here.This is just the kind of use case I had in mind. Something I've considered is publishing a mini version with only the json.encode_string function, as that's enough to create an array of JSON-encoded strings and use a hard-coded template with printf to insert the JSON string values.
That would be a fraction of the overall json.bash file size.
I definitely like the idea of a goland/rust implementation, there are certainly things I could improve.
So the argument syntax escapes by repeating a character rather than backslash. I chose this because with backslashes escapes it would be unclear whether a backslash was in the shell syntax or the jb syntax, and users may end up needing to double escape backslashes, which is no fun! Whereas a shell will always ignore two copies of a character like =:@.
The downside of double-escaping is that the syntax can be ambiguous, so sometimes you need to include the middle type marker to disambiguate the key from the value. But the type can be empty, so just : works:
$ jb ===msg==:==hi=
{"=msg=":"=hi="}
In the key part, the first = begins the key, the == following are an escaped =. The first = following the : marks the value, and everything after is not parsed, so =hi= is literal.When you have reserved characters in keys/values (especially if they're dynamic), it's easiest to store the values in variables and reference them with @var syntax:
$ k='=msg=' v='=hi=' jb @k@v
{"=msg=":"=hi="}Still, bash can try to keep up using json.bash. :)
$ source json.bash
$ declare -A greeting=([Hello]=World)
$ json ...@greeting:{}
{"Hello":"World"}
... is splatting the greeting associative array entries into the object created by the json call.Without the ... the greeting would be a nested object. Probably more clear with multiple entries:
$ declare -A greeting=([Hello]=World [How]="are you?")
$ json @greeting:{}
{"greeting":{"Hello":"World","How":"are you?"}}
Vs: $ json ...@greeting:{}
{"Hello":"World","How":"are you?"}This example uses the pattern of setting an out=varname when calling a json function, the encoded JSON goes into $varname variable. This pattern avoids the overhead of forking processes (e.g. subshells) when generating JSON.
Otherwise you can use the more normal approach of jb writing to stdout, and capturing the output stream.
$ jb dependencies:json='["Bash","Grep"]'
{"dependencies":["Bash","Grep"]}
$ jb foo=bar bar:json='"this is a well formed string"'
{"foo":"bar","bar":"this is a well formed string"}
And then you can indeed use command substitution to nest calls: $ jb foo:json=$(jb bar=baz)
{"foo":{"bar":"baz"}}
It works even better to use process substitution, this way the shell gives jb a
file path to a file to read, and so you don't need to quote the $() to avoid whitespace breaking things: $ jb foo:json@<(jb msg=$'no need\nto quote this!')
{"foo":{"msg":"no need\nto quote this!"}}
Another option is to use jb-array to generate arrays. (jb-array is best for tuple-like arrays with varying types): $ jb dependencies:json@<(jb-array Bash Grep)
{"dependencies":["Bash","Grep"]}
And if you use it from bash as a function, you can put values into a bash array and reference it: $ source json.bash
$ dependencies=(Bash Grep)
$ json @dependencies:[]
{"dependencies":["Bash","Grep"]}If I started from scratch now I'd use a compiled language that could produce a single static binary and start with really low latency. I'm pretty sure jo must not be tuned for startup time, if they optimised that they must be able get it way faster than bash can start and parse json.bash. I was pretty surprised that bash can startup faster!
The codebase is basically at the limit of what I'd want to do with bash, but there are features I could add if it was in a proper programming language. e.g. validating :int number types, pretty-printing output, not needing the :raw type to stream JSON input.
Thanks for the heads up on Shellcheck, I'd be happy to take a PR if you'd like to.
The same using jo would be like this, which I find harder to type and remember:
jo -- -s id=42 -n size=42 -s surname=null data=null
{"id":"42","size":42,"surname":"","data":null}
Notice that surname comes out as the empty string though, I think this must be a bug in jo!Normally, detecting errors on the other end of a pipe requires care in a shell environment (e.g. retrospectively checking PIPESTATUS). I used an approach I've called Stream Poisoning. It takes advantage of the fact that control characters are never present in valid JSON. When jb fails to encode JSON, it emits a Cancel control character[1] on stdout. When jb encounters such a character in an input, it can tell the input it's reading from is truncated/erroneous. This avoids the typical problem of a pipe silently being read as an empty file.
I've got a page explaining this with some examples here: https://github.com/h4l/json.bash/blob/main/docs/stream-poiso... I can imagine using control characters in a text stream being rather controversial, but I feel it works quite well in practice.
I presume you have some css-related projects/tasks in mind that you'd like to be able to tackle yourself. I would pick one of those and jump in at the deep end. Although a small goal like restyling something that already works is probably better than starting from scratch.
For a completely abstract task, you could try developing a stylesheet for markdown html output.
Vanilla CSS is good enough (and browser support consistent enough) now that you don't need to use any compilers/frameworks to get started.
If you find yourself struggling for design inspiration, try to re-produce an existing design without seeing how they did it.
The domain of styling html is so large that there's no chance of learning everything in theory then applying it, I think it's best to learn by doing.
FYI the category link right under the title links to a different category to the link below, and the first link only contains one post.
I've not used xquery enough to know if it can be succinct enough to be used as jq's language can.
For sure the Saxon cli could be made a lot more user friendly if it followed normal conventions.
Where's the passive aggression? Later on, the article hints that before this played out, Aaron was annoyed at Jim. He intentionally spoiled the dinner (e.g disrespecting the effort Jim invested) by staying on the phone, but because he pretended to be unable to avoid the call it's passive — he can claim not to have spoiled it intentionally.
In contrast, it would be regular aggression if he'd thrown the pasta into the sink.