A terminal case of Linux
fasterthanli.me
fasterthanli.me
* https://jdebp.uk/Softwares/djbwares/bernstein-ptyget.html
There's even scope for observation that, like the number of processes that get spawned and instructions that get executed to increment an integer shell variable by 1 (in the traditional way using expr), it's taking four extra forked processes just to write a coloured shell prompt.
Hacker News comments don't get beyond the useless use of cat aside in the third paragraph. (-:
unbuffer rg TODO src/
or find . -iname '*.md' -exec unbuffer ls {} \; | less -R
or unbuffer cowsay --rainbow 'Hello from a very colorful cow!' | sudo wall --nobanner
and so on.If you make an alias or an abbreviation for your shell, it ends up being more convenient than figuring out all the flags for making piped output look like interactive input for each individual program you might want to use that way. :)
--
Ew. Sorry, but no.
find . -iname '*.md' -print0 | xargs -0 unbuffer ls {} \; | less -R***Your correction fixes both of those issues (the former through using xargs and the latter by splitting the arguments passed to it on the nul character instead of the default (newline, right?)).
find does not pass arguments through an intermediate shell, so there are no quoting concerns with regard to spaces in filenames, etc.
And with the form of -exec terminated by a ; it runs a single command for each matching filesystem entry, so there will be no issues with exceeding the maximum argument size.
You can verify via strace:
$ touch 'a b.md' 'c d.md'
$ strace -fe execve find . -iname '*.md' -exec unbuffer ls {} \;
At the end of the exec chains you should see the following: execve("/bin/ls", ["ls", "./c d.md"] ...
execve("/bin/ls", ["ls", "./a b.md"] ...
The filenames are being passed through spaces and all.However if this were a real scenario, a better solution might be:
find . -iname '*.md' -exec unbuffer ls -1 {} +
(Look up the -1 flag for ls, and the + variant of -exec for find for details)1. It allows you to drop context quickly and focus on algorithm. You write "cat filename" and you can free your mind and focus on the rest of the pipes. Without cat, you have to keep filename in your mind while you write the first command.
2. It puts filename closer to the beginning so when you need to repeat for few more files, you just press home and one Ctrl+right, whereas without cat, you would have to move several times right depending on how many switches first command needed which is always random. Plus most of the back and forth switch editing will be in the first command and filename would just get in the way.
3. It works the same for every app. Some apps need file as last parameter, some needs -i, or whatever switch, some use some other random scheme. Cat is always the same.
1 -> 2 -> 3 is easier that (2 <- 1) -> 3
In bash, type:
< /etc/resolv.conf grep nameserver
(That < is not a prompt, that's the less than symbol)< /etc/passwd while IFS= read line || [ -n "$line" ]; do printf '%s\n' "$line"; done
Meanwhile, in ZSH...
Meanwhile, in languages that do not need IFS= and || [ -n "$line" ] checks...
cat f | grep nameserver | otherproc…
…is more consistently compositional.'+ 2 3' becomes 'add 2 and 3'
In any case, it's a silly objection.
+ 2 3
is naturally how you'd write addition if addition were a standalone Unix command!(You can't just remember what you learned in math class when writing a computer program, because now we have bitwise and, or, xor, left shift, right shift, equality, pointer dereferencing, assignment, logical and, or, modulo, pre-increment, post-increment, bitwise not, logical not, etc. So you have to learn it all again. And it's different for every language.)
Put everything up front, and suddenly you don't have to think about this anymore. (+ (expt 2 3) (/ 4 2)). The computer just does what you say and there are no rules to memorize.
Personally, I always use RPN calculators (dc is my favorite), because I don't trust the calculator to remember operator precedence. They usually do it right, but it's difficult to reason about. (The example would be "2 3 ^ 4 2 / + p" in dc.)
In Indian schools it used to be BODMAS, for Brackets Of Division Multiplication Addition Subtraction, for operator precedence, for which our teachers told us the mnemonic was badmaash, meaning a scoundrel or a villain, in Hindi. Guaranteed to make kids remember it. :)
Of, as in half or a quarter or ... of some other number.
I think FORTRAN copied it first. Which makes sense, because of the original scientific target market for FORTRAN, represented in its name derived from “formula translating system”. Translating “a sufficiently rich mathematical language”, as Backus put it, was an explicit goal as early as 1954.
C then (presumably) just followed the convention established by FORTRAN.
what is unnatural is the complicated case specific precedence rules mid notation requires. rpn still has precedence rules but because they are simple it appears that there are none and because they are universal there is no confusion as to where any new operators fit in.
I wonder if anybody has ever written a linguistic history of math notation and how it ended up the way it did.
(x-3)(x+2)-8 > 2x^2+3
x^2-x-8 > 2x^2+3
x^2+x+8 < -3
x^2+x+11 < 0
vs x 3 - x 2 + * 8 - x x * 2 * 3 + >
how would I simplify the expressions here? Maybe it's possible without transforming to infix but I can't do it.Sure, few seconds isn't a lot, to someone who opens terminal once a year.
Additionally, if you're using a nicer colorizing pager, like bat, it'll automatically send the output to your pager for you when it detects that the output wouldn't fit on screen.
For both of those reasons, I almost never use `less` directly on a file.
Obviously you don't have to use vim, any other editor or whatever can be used.
In zsh, there are equivalent expansions:
!!:1 previous command line, arg 1 (zero-indexed)
!!:$ previous command line, LAST arg
!!:1-$ previous command line, args 1-end (zero-indexed)
So, e.g.: cat /etc/passwd /etc/shadow /etc/group
vi !!:1-$
(becomes) vi /etc/passwd /etc/shadow /etc/groupIf I wanted to do the cat/vim switcheroo you described, I'd more likely do:
<UpArrow><Esc>vcwvi<Esc>:wq<Enter><Enter>
Which is a greater number of keypresses, but more flexible and requires less thought....as they say: Unix is user-friendly — it's just choosy about who its friends are.
:set t_te=
Useful because vi makes it more convenient to select the chunk of file I want to view after exiting. More convenient than cat/head/tail at least. Competitive with more. :)It’s like an “article” or “preposition” in English. Arguably not required, but in practice invaluable. One could argue that shell could have been designed better to not need it but, meh, that ship sailed. Uses of “cat” are ubiquitous and excellent. Not sure why people remotely care when there are bigger problems to worry about.
I didn't agree with it - it's an ok rule of thumb in moderation but I don't think HTML string templates are a good example. But, `cat` is absolutely the perfect example.
I use shellcheck which automatically corrects all my "useless" uses of cat, and I can tell you there's no more common error in other people's code that it complains about. And the benefits of "fixing" this error are always so marginal, the resultant code so much less readable.
What an utterly useless thing to get pedantic about.
So as long as people don't cry 'useless use of cat' we get along ;-)
4. You are just ctrl-a away from changing source
the common use for me for example
cat f.log |grep sth # okay it shows in history; C-a
tail -f f.log | grep sth # let's try to repeat it live; C-a
zcat f.log.1.gz | grep sth # no dice, let's try to dig a bit in history < file command
Is perfectly valid.To me, it just makes sense to put input on the left of a pipeline.
IDK why others don't, but :shrug:
$ <example.json jq
[
1,
2,
3
]
…but if you are using a POSIXish shell, which I do not (fish).get file data and put it on the pipe: cat get pipe data and put it in a file: tee (with huge caveat you have to do something with stdout so you still need to mess around with the redirection operators. tee does not really gain you any normalization. Sigh. I would make a tee that is just "tee > /dev/null" but I am used to it at this point. Stockholm syndrome?)
<take this file> | <do this to the file> | <then do this>
And UNIX philosophy says that is a good thing. I takes a universal UNIX technique (the pipe) and the programs just have to concentrate on processing input and output.
Sure you CAN use the args. Look up the fucking specific flag and syntax after the bajillion flags in the man pages (which all seem to lack good examples that would communicate this much more quickly.
But the cat | jp seems FAR more UNIXy. I agree, it is stupid to criticize cat there. This is pointless fad thinking by the author. Ignore it. There MAY be certain performance advantages possibly in certain high volume conditions like large files or something similar, if the program has "better" file processing than cat + a pipe. Maybe.
Anyway, I really like the data flow of a pipe command like:
cat <somefile> | sort | head -n 10
Meanwhile, this is how a programming language would look (one liner version): head( sort( cat(<somefile) ), 10)
The logical flow of that, where you get data, sort it, then head it, is natural in the piped instance, but the practically every language ever version of it you need to parse the expression first, extract the inner steps, subparse / resolve the args to the head, then get the value.The expression is "inside out" in a normal progarmming language. You have to turn it inside out: PUSH head op on the stack, PUSH sort onto the stack, resolve CAT, POP sort op, POP stack op.
The CLI + pipes has no stack, just a handoff
I have started doing "data first" programming a lot with say Groovy, where you can do:
String passed_in_filename = "/path/to/file"
passed_in_filename.
with { File.lines(it)}.
with { it.sort()}.
with { it.head(10)}
with a lot of the I/O or transform data code I write. (The with construct in groovy takes the output of the invoked code and then passes it to the provided closure, so I can chain the closures in the natural way that piped output works on the CLI)In bash (don't know about other shells) the < operator sets the stdin of the process to the given file.
< filename.json jq
Here, data is read directly from the given file.As for the | operator, the stdout of the left-hand process and the stdin of the right-hand process become a pipe:
cat filename.json | jq
Here, data is read from file, copied to a pipe buffer, the other process is
notified (in most cases, by being woken up) that there is incoming data and then it finally reads the data.Not that any of this matters in today's world of pocket supercomputers but I just don't see how any of your points back up the useless use of cat. It's still useless :)
While Bash has all sorts of nice bells and whistles for various use-cases, I really think they are only worth relying on when you are doing something highly specific. In general mastering wildcards and pipes is sufficient for the vast majority of situations, I think for everything else it's better to use commands that people can easily search or find the man page for.
In the world of pocket supercomputers as you called it, interface is king. So I'd argue that if anything, `<` is the useless one here when we can could just have just used the more semantically obvious cat and pipe instead.
That function takes no argument. I've never seen initializing a single variable and returning the result of that initialized variable from a function call taking no argument called "memoization".
Memoization is a specific type of caching with a space/time trade-off where the result of a particular (set of) input(s) are cached.
I find it quite a stretch to call initializing a single integer for a function taking no parameters "memoization". I'd just say the function's return value is "cached".
I mean: by kinda, sorta, bending the definition of memoization (for example by changing "caching the function's results based on the inputs" to "caching the only result for there's no input to the function") you could say that it is actually memoization but then I'd argue this is not a great example of what memoization is.
I may be wrong though.
No hill to die on (and I enjoyed TFA!) that said ^ ^
> an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls and returning the cached result when the same inputs occur again
"the same" could be none, default, or null, I think.
Anyway. Is there something better than just screen-shotting your terminal window and making PNGs or GIFs for stuff like this?
There are some long standing bugs against the aha tool pointing out that it does not even come close; including not coping with switching to and from the alternate display buffer. No-one seems to have yet noticed that it doesn't recognize ED or EL or any relative cursor motions, so will have serious problems with things like ZLE in the Z shell, or just plain clearing the screen; or that it doesn't handle 24-bit colour in the same ways as some popular terminal emulators do.
Screenshotting would involve reading a Linux KVT /dev/vcsa file, or a display file with my terminal emulator, or using a Kitty plug-in to get at its display buffer contents, or attaching to a screen/tmux server and reading out its display, or similar (if even possible) for other terminal emulators.
Ironically, BRLTTY can do some of the aforementioned. But it most certainly doesn't spit out HTML.
Still image: https://github.com/nbedos/termtosvg (dead, unknown if usable?)
I’ve come to appreciate “dry”, technical guides void of overt personality. I’m not a fan of “Why_’s Poignant Guide to Ruby” in the least, but there is a visceral element in its whimsicalness (and other why_ materially) that similar efforts don’t compare to (I count the listed guide here among this). For me, why_’s work is an outlier in this “literary tech writing” genre. If I ever intended to learn the language, I would contemplate scrubbing out the comic strips if I couldn’t find a better alternative guide.
I think that style could work well when you're doing say a post-mortem of some failure, where you want to show the whole train of thought that lead to it or to finding a fix, but for this kind it just doesn't work very well
I guess I’m getting too old for this - I’m leaving this to the younger crowd !
This is why it was such a struggle for me to get through Godel, Escher, Bach as well. If I'm engaged on the topic, I want the author to get straight to the point, not to try to anticipate my questions or lead me with someone else's potted questions.
> Anyway. Is there something better than just screen-shotting your terminal window and making PNGs or GIFs for stuff like this?
I've done it manually in HTML/CSS for documentation, to preserve copy-pasteability. It's straightforward: body bgcolor, and font-family (something fixed width), then a bunch of spans with colors for syntax highlighting.
Would love to find something automatic though! E.g.:
command | ttyansi2htmlcss | pbcopyThanks for the link!
There is and it's been on my TODO list forever. In fact, the article you just read (congrats on getting through my narrative devices) was written /while tackling that/.
https://asciinema.org does it for "moving pictures", it shouldn't be too hard to do it for stills.
> Well, showing a less invocation is actually quite annoying, so instead we'll just use cat instead.
If `cat` is a pager on your system (which is the only way I make sense of this)... Why? And what's so annoying about paging to `less` (retaining colors) than your paging `cat`? I also don't see why the author doesn't `< $FILE | jq $FILTER` instead instead of `jq $FILTER $FILE`. Finally , if it makes the process easier for the only user of the script, that `cat` served a purpose and was not useless.
And the comments about useless cat is just a gag/OCD.
[1] https://tokio.rs/tokio/tutorial/select (the section titled "Loops")
Upon that the child application closing, you loop reading from the primary using a synchronous but non-blocking read, until you finally get back a zero byte read. Since the child process is now dead, and nothing else should be writing to the TTY, once you get back a single zero byte non-blocking read, you know you have all the data.
Ideally in C on Linux, you would structure this code a bit differently. (I'm only talking about the C API here, because I don't know rust or tokio well enough to have any real chance of knowing how to do this correctly for those.)
To avoid having to use multiple threads, or handle SIGCHLD, you would fork with the clone() call and CLONE_PIDFD, to get back a PIDFD. [1]
Now you can use poll() against both the parent TTY descriptor and the pidfd. Regardless of which-one became ready, you will do the synchronous non-blocking read loop, to get any data available. Then you conditionally run the process exit code if was the pidfd that became ready, including reaping the child process with wait(), and closing the pidfd. Otherwise loop back to the poll() call.
Needless to say this sort of thing is extremely complicated, as there are many potential race conditions, and getting things wrong here is quite common. I've seen multiple languages have bugs in their code for reading just normal process output, and many lack built-in support for running in a pty, which has pretty much all the same considerations.
Footnotes: [1] You could also do a normal fork, and then get a PIDFD from the resulting PID using pidfd_open(). This also works fine, but the lack of a glibc syscall wrapper for pidfd_open may be annoying, and it is an extra sycall. This is safe though, as we are the parent process and until we reap the child by calling one of the wait() functions, the PID will not be reused.
> What? I see green.
Smart ass bear. Good article as always.
Colored output feels like something a syscall should handle. Something like setcolor, unsetcolor, separate from the typical printf functions. At least escape sequences are easy to detect and remove, albeit a hassle.
and then I shudder and walk away.. the variety in the answers is alone a thing to wonder
Nothing grinds my gears like the DARK BLUE on BLACK colors in the UEFI shell.
You can set the background color to. So if you set the background to dark grey you can assume the background is dark grey. Assuming the terminal supports color that is.
<https://invisible-island.net/xterm/xterm.faq.html#color_by_n...>
Researching this comment I found it's also possible to toggle urxvt colour themes on the fly, via Stack Exchange: <https://unix.stackexchange.com/questions/296257/different-co...>
I'm not sure that this is also possible on, say, the Linux console itself, though I suspect it may be.
I too had a first impression over a quarter-century ago the first time I saw a colourised 'ls' output on my first freshly-installed Linux system that they were garish, and have often griped about dark-blue-on-black (which is how I'd discovered the palette configurations referenced above). But I quickly found the feature useful and helpful and miss it when on a true monochrome device (of late: an e-ink tablet with Termux installed for a Linux userland).
On Linux, if something annoys you, there's almost always an alternative or configuration to alleviate that.