Something I learned recently is that the Bourne shell (and by extension, bash and POSIX's sh) have syntax inspired by Algol 68 (source [0]), which explains some of the funkyness. One thing I've been doing recently is writing scripts in rc, the default shell for plan9. It's a bit saner syntax-wise IMO. Versions linked against readline have file-based completion, but it's otherwise not quite robust enough for me to switch away from fish as my default, but it has some things I prefer over both bash and fish.
I encourage people to give rc and awk a shot, they're both small and learnable in an afternoon!
> One thing I've been doing recently is writing scripts in rc, the default shell for plan9. It's a bit saner syntax-wise IMO
I'll stand on the point that if you're gonna forego (ba)sh compatibility, 95/100 times you might as well write Python scripts. Shell syntax generally sucks, and the only reason we still roll with it is legacy code, universal compatibility, and pipes.
As someone who worked with Python professionally for like 8 years, I wholeheartedly disagree with this. Python is an ok scripting language but it's an awful command script language. The extra ceremony to just invoke another command is a complete non-starter IMO. Ruby handles the cases where a shell script makes sense way better than Python does and I still wouldn't pick Ruby. Rc's syntax is better than ba(sh), which is the point: you get things like pipes and easy commands without some of the cruft.
As someone who also has written my fair share of Python, I completely agree. Piping and subprocess handling are my two main complaints as well.
Managing concurrent processes is arguably also easier in shell scripts, in that you can just append “&” to run stuff in the background and “wait” to sync.
I've grown rather fond of bash in my current role. I work mainly on developer tools and CI pipelines, both of which mean gluing together lots of different CLI tools. When it comes to this kind of work I think it is quite hard to beat the expressiveness of shell scripting. I say this as a former hater of bash and its syntax.
Much credit to copilot and shellcheck, which have made complex bash ever the more write-only language than it already was.
> I've grown rather fond of bash in my current role. I work mainly on developer tools and CI pipelines, both of which mean gluing together lots of different CLI tools. When it comes to this kind of work I think it is quite hard to beat the expressiveness of shell scripting.
Every time I have to express logic in YAML, I miss shell. Shell’s really not great, and it could be improved upon (my vote? Tcl), but it’s so much better than where the industry is these days.
Sure, you can generate the YAML file, but it’s not generally possible to trigger that from the system which wants the YAML file, and you don’t get to integrate with the system’s configuration. Often these systems honestly think that their approach of logic-templated YAML is preferable to a script — for example Helm’s templated Kubernetes YAML.
Few things grind my gears worse than _templated_ yaml. It equivalent of building up a json object using only string concatenation. Most people would raise their eyebrows at that, yet templates yaml is seen as very normal.
I think in part it's a skills mismatch - a lot of devops/sysadmin type folks I encounter, while very talented, are not prolific coders. So code-forward solutions like jsonnet, starlark, dhall, nix, etc. are rather unfamiliar familiar.
It doesn't help that all but one of those languages mentioned are odd little functional languages, increasing the familiarity gap even further.
Bash actually has more warts than competing shells because of its historic stance.
My bugbear is that "alias p=printf" works well in any POSIX shell script, including bash when it is invoked as #!/bin/sh - but when called as #!/bin/bash, the alias (used in a script) fails with an error.
While the Korn shell managed to evolve and comply with the POSIX standard, bash decided to split the personality, so one solution to the above alias failure is to toggle POSIX mode.
Bash was forced to do this, to keep a decade of shell scrips that were written for it working. Pity.
The standard for the POSIX shell looked very hard at Korn, and credits it. Bash is not mentioned.
Thanks, I had no idea. I guess I've never used aliases in scripts, but I would've assumed that they'd just work the same as in interactive mode. Good to know.
Functions don't work everywhere. Bash functions only work in the current shell context unless exported via an `export -f myfun' statement in between the function declaration and downstream sub-shell usage.
Working example:
pzoppin() {
printf 'echo is for torrent scene n00bs from %s\n' "$*"
trap "printf '%s\n' <<< \"$*\"" RETURN EXIT SIGINT SIGTERM
}
export -f pzoppin
echo -e 'irc\0mamas donuts\0starseeds' \
| xargs -0 -n 1 -I {} /usr/bin/env bash -c '
echo hi
pzoppin "$*"
echo byee
' _ {}
The above will fail miserably without the magic incantation:
`export -f pzoppin'
Why'd they design an otherwise perfectly usable, mapless language without default c-style global functions? :)
I typically just give up on weird corners of shell when I find an working version of the same.
For instance -- why would you use "alias" when you can make a function? The syntax is a little weird with functions, but it's a lot more clear what's going on.
The same goes for "test" vs the seeming magic of [ where it seems like [ is language syntax (it's a single character!) when in fact ... it's just another executable that communicates with logic evaluation like anything else (like grep or false).
alias MY_VAR_MODIFIER='local foo=bar'
MY_VAR_MODIFIER () { local foo=bar; }
Calling the alias by the name will set the callee's variable foo, while calling the function does nothing (local foo is local to the function and never leaves scope).
It also works similarly for working with the set ($@). You can do `set --` stuff and it works on the scope of the callee.]
Aliases on most shells also don't need to be fully valid _before_ expansion. You can alias a compound command:
alias foreach='for EACH'
foreach in $MYVAR #perfectly valid for most shells.
Only ksh93 will complain about it, it requires aliases to be complete valid shell programs.
Finally, alias calls don't appear in xtrace (set -x). Only the final expansion will appear.
Gluing together tools with shell scripts is a significant cause of CI failures in my experience. There's no reason to do it. Use a real language - at least Python, but my preference is Deno because it's not dog slow and you don't have to deal with venv.
The subprocess.run args argument is a list of strings, not a single string with whitespace-delimited parts, so you're all good quotingwise. This is now effortlessly a lot better than what you get with bash in terms of how brittle things are!
Supply check=True and the script will barf on subprocess failure. Another useful upgrade.
Take a look at https://sh.readthedocs.io/en/latest/ for a very usable solution to easily run other processes in Python. It has made my life a lot easier whenever I've had to migrate a shell script to Python.
Shellcheck will prevent you from making any egregious whitespace errors. You can dynamically build up an arguments list using bash arrays. Errexit and pipefail options prevent you from ignoring errors.
I agree that some work is better suited to a real programming language, however the driving force isn't the problems you speak of, which are trivial, it's when the logic/control flow of the overall problem becomes more complex.
> Maybe it makes more sense to post the link to YT, as you did in 2022
Based on the comments there, it seems like OP posted this current (shellhaters.org) link then too, and it's the mods that replaced it to point to the YouTube video. Hopefully the same will happen this time around as well.
I tried following the slide deck (to get an idea of the content), but that's pretty rough too. The page reloads itself constantly and is very inconsistent with responding to input keys, it's not a pleasant experience.
Thanks for the (indirect) link to the YouTube video, will give that a try.
A brilliant historical artifact, and the irony that macOS has been Unix for most of the Mac's existence, and that Windows has included POSIX and/or Linux for most of its existence, is not lost on me.
The Dennis Ritchie anti-forward makes it even better:
> The rational prisoner exploits the weak places, creates order from chaos: instead, collectives like the FSF vindicate their jailers by building cells almost compatible with the existing ones, albeit with more features. The journalist with three undergraduate degrees from MIT, the researcher at Microsoft, and the senior scientist at Apple might volunteer a few words about the regulations of the prisons to which they have been transferred.
I'm not familiar with the landscape - is there a "compiles to reasonably-readable [ba]sh" language that's gotten any traction anywhere? That seems like at least an interesting possible solution.
The only problem I have with it is that the author is terribly fond of weird self-referential puns and wordplay, and I find the docs (and posts and comments) he writes to be basically unreadable.
Even the project names: osh, and ysh, and oils, where osh is short for Oil Shell, but so is OilS I think...
It makes it really annoying and difficult to read, IMHO. Maybe it's just me.
The trouble is that all the existing xNix GUI shells suck. They suck in widely varying degrees, from ones that could suck a bowling ball through an iron water pipe, like GNOME and KDE, to ones that could merely suck a golfball through a hosepipe, like Unity and Xfce, but they all suck.
There have been GUI shells that didn't perceptibly suck at all, such as early versions of the Windows 9x shell, classic MacOS, and Acorn RISC OS, but they're all largely dead and gone now.
Well, I meant people should use a real programming language. Also, I'm not sure why "bash with a real programming language syntax" isn't a thing. Apparently last time this was tried was the "C Shell" which had c-like syntax? Why don't we have a javascript-like syntax for a shell yet?
https://www.youtube.com/watch?v=olH-9b3VJfs
Something I learned recently is that the Bourne shell (and by extension, bash and POSIX's sh) have syntax inspired by Algol 68 (source [0]), which explains some of the funkyness. One thing I've been doing recently is writing scripts in rc, the default shell for plan9. It's a bit saner syntax-wise IMO. Versions linked against readline have file-based completion, but it's otherwise not quite robust enough for me to switch away from fish as my default, but it has some things I prefer over both bash and fish.
I encourage people to give rc and awk a shot, they're both small and learnable in an afternoon!
[0]: https://doc.cat-v.org/plan_9/4th_edition/papers/rc