Ask HN: What to use instead of Bash / Sh for scripting?
I'm at the point where I feel a certain fatigue writing Bash scripts, but I am just not sure of what the alternative is for medium sized (say, ~150-500 LOC) scripts.
The common refrain of "use Python" hasn't really worked fantastically: I don't know what version of Python I'm going to have on the system, installing dependencies is not fun, shelling out when needed is not pleasant, and the size of program always seemingly doubles.
I'm willing to accept something that's not on the system as long as it's one smallish binary that's available in multiple architectures. Right now, I've settled on (ab)using jq, using it whenever tasks get too complex, but I'm wondering if anyone else has found a better way that should also hopefully not be completely a black box to my colleagues?
105 comments
[ 1.8 ms ] story [ 36.1 ms ] threadWhat do you think about editing the title to include “for scripting”? I thought you were asking for recommendations like zsh or fish when I first clicked
Deployment or installation is just one scp away.
not rewriting, just when It needs large refactoring, or is currently a mess, or brand new.
Still, when we're talking about casual "scripts" even this extra step seems a little bit burdensome
Though in all those cases, for me, all alternatives are just as bad or worse.
Getting ansible running on a windows server is... unfortunate. Ensuring that your bash script runs on OSX is easier, but I've had lots of unexpected issues with tools like grep or find working just a tiny bit different. And so on.
https://shlomif.github.io/open-source/anti/csh/ has a link to it, and other things.
You might not adopt it, but it can give perspective on the pros and cons that comes with Bash.
It is cross-platform so you can use your scripts anywhere.
Don't use python or go or ruby or whatever as those are not scripting languages.
IMO Bash is horror and should die already.
How big really? Can't be more then 200MB? Should be less then minute to download and install? If you have docker, can you go that route?
Also, if you account the fact that you have variants of many other popular tools included OTB is that really a downside ?
That is certainly different concern, you want fast and practical script programming and why would you care if it has first time latency of couple of minutes if you are set good forever after that ?
The shell was running from tmpfs (so basically a RAM disk). I have a decent processor and an NVMe SSD, and this is what I see (it was tested 10 times in a row to pull everything into the page cache properly, and the numbers are pretty stable):
More than a second to execute an empty 'script', and even longer to get to the interactive prompt (which is harder to time properly so I'm not publishing any numbers).It's even slower on an older Windows laptop which I have lying around (starts in about 3-4 seconds). Absolutely terrible for an interactive shell/script execution engine.
Just for comparison's sake, the same laptop can run an empty bash script in 2-3 milliseconds. This is very important when running build jobs, for example.
There's no defender (or anything similar) on that other machine. It also has an SSD. With an older HDD powershell started in about 10-15 seconds there (while cold startup time for bash was something like a second, and instantly afterwards).
Just use a proper scripting language.
Verbosity of powershell is optional, I don't use it.
> consistency that comes with all Microsoft designed products.
Oh, lets not troll here.
Why is that a disaster? What problem has it caused you that wouldn't have happened if it wasn't an executable?
[[ is a non-executable version of [. Does that solve your problem?
Its disaster because launching process to achieve simple math expression is just ridiculous, besides being slow and besides having to terminate it with ;. Its wrong on so many levels I don't even know where to start, they must have been smoking some nasty things back in time.
That's why you don't do that. $(( math goes here ))
In any case, worrying about subprocesses and their slowness in a shell is completely moot. Shells aren't built to run fast, and are rather built to work with executables conveniently.
> besides having to terminate it with ;
Is that really a complaint? Does that bother you a lot? This seems so petty, I'm a bit lost for words.
[ is a regular command because the control structures of the shell are flexible enough to support any command for their conditions.
I don't know if you're asking for the shell to make a super-special exception in its syntax for that particular command just so that people won't have to type ; under certain circumstances, or if you're asking for control structures to be more restricted and allow only [ instead of any command for their conditions.
> they must have been smoking some nasty things back in time.
So far, you've given the impression of someone complaining that screwdrivers really suck for inserting nails. I wonder if the issue is that you've never seen hammers before or if you've never seen screws before. Or maybe the issue is you believe each tool should be equally fit for everything?
And even if not, so what? Every argument you make is a shifting of a goalpost.
Also, for scripting stuff, my (readable, extensible) perl is about 10% as long as other people’s python, so 500 lines of perl ends up being 5000 lines of python. That creates a huge difference in maintainability and correctness.
With code golf, my perl can be 10x shorter than that, but then it is neither readable nor extensible, and doesn’t scale to 500 (or even 50) lines before becoming more trouble than it’s worth.
As the song says: "But I still... haven't found... what I'm looking for."
It still has other python-style problems, like epic dependency graphs, with all the compatibility and bitrot nightmares those imply.
usr bin env ts-node
On phone but you get what I mean
I tend to use common lisp but I'm learning guile scheme and will probably move to that instead.
How would Nix help me make Haskell into a script?
This was on HN the other day. Nix allows you to crunchbang a `nix-shell -p` kind of expression.
1) There aren't two disjoint versions to juggle
2) Dependency installation is clean and contained
3) Dependencies can easily be installed locally or globally, depending on your preference for isolation vs reuse
4) Lots of packages are available for doing cross-platform (read: Windows compatible) operations
5) JS makes it really easy to do efficient async tasks, for example high-IO scripting involving reading and writing lots of files and making network requests (without any dependencies, I might add)
At my last company I converted all our shell scripts to Node scripts when the pandemic hit, so they'd work predictably on Windows machines, and it was great. These days I'd do it that way to begin with.
I am familiar with and use quite a bit of js on the front end, but how is node for shelling out? This is something I find Python to be quite terrible at.
Calling shell commands is reasonably straightforward:
However, you can also get cool packages like this to use instead: https://github.com/shelljs/shelljsI will admit though that in most of the cases where I've used it I didn't really do any shelling-out, so I can't speak much to how that experience scales in practice. Mostly I was either 1) doing self-contained logic with files, web requests, etc, or 2) delegating out to JS build tools (which even when they have a CLI, almost always expose a nice JS API too). Ymmv, etc.
$output = `ls -lh /usr`;
I don't think I can remember all the boilerplate of using the wrong language for the job if I only happen to write that once a month or less.
Even PHP is easy.
$output = shell_exec('ls -lh /usr');
The Node version was some 90 LoC but if I included the dependencies it came to about 5 MB. Then I rewrote the same script in Python3 and it was 60 LoC and no dependencies that needed to be installed separately.
I will admit that the JS code looked _a lot_ cleaner than the the Python code though.
Really you should pick whatever you currently have in your codebase. We happen to have both Python and Node, with the languages generally being developed separately by different devs. So we’ve got tools in both, but a Node developer is unlikely to ever need/use the Python tools (although they can all run in Docker anyway). What annoys me a bit is that pre-commit requires a Python env to bootstrap itself, but it can install nvm without any fuss.
It's not the best fit for everyone but it has served me well.
https://github.com/babashka/babashka
Most OSes come with relatively modern versions of either. Pretty much everyone is or should be familiar with either. It’s not like it’s some Lispy thing.
It's really easy to write dangerous shell scripts ("${@}" vs ${@} for example) and also easy to write dangerous Python scripts (cmd="{}; {}").
Sarge is one way to use subprocess in Python. https://sarge.readthedocs.io/en/latest/
If you're doing installation and configuration, the most team-maintainable thing is to avoid custom code and work with a configuration management system test runner.
When you "A shell script will be fine, all I have to do is [...]" and then you realize that you need a portable POSIX shell script and to be merged it must have actual automated tests of things that are supposed to run as root - now in a fresh vm/container for testing - and manual verification of `set +xev` output isn't an automated assertion.
ansible-molecule is a test runner for Ansible playbooks that can create VMs or containers on local or remote resources.
You can definitely just call shell scripts from Ansible, but the (parallel) script output is only logged after the script returns a return code unless you pipe the script output somewhere and tail that .
> manual verification of `set +xev` output isn't an automated assertion.
From "Bash Error Handling" https://news.ycombinator.com/item?id=24745833 : you can display the line number in `set -x` output by setting $PS4:
But that's no substitute for automated tests and a test runner that produces e.g. TAP output from test runner results: http://testanything.org/producers.html#shellI was never a Perl monk by any means but I knew enough to get by. And why did I not even think about it? I haven't coded Perl in years, but if my memory serves me correctly, bundling extra dependencies in Perl was a heck of a lot less painful than Python too.
Really sad it lost so much traction in the last decade, I partially blame it on the failed Perl6 endeavour. I also mostly use Python these days but looking at my old Perl scripts Python really doesn't come close in terms of succintness.
> shelling out when needed is not pleasant,
Handled properly because the language was built ground up for Ops tasks. There is even a syntax for "run a command and parse the output". Rationale and examples - https://ngs-lang.org/doc/latest/man/ngswhy.1.html
Currently NGS supports Linux and MacOS.
> smallish binary
Unfortunately there is no static build yet so can't check this checkbox
Next Generation Shell - https://github.com/ngs-lang/ngs
Alternative Shells list by @chubot - https://github.com/oilshell/oil/wiki/Alternative-Shells
[0] https://rust-script.org/
[1] https://crates.io/crates/dialoguer
[2] https://crates.io/crates/cmd_lib
If you want strong typing (are you scripting in the large?), then, almost by definition, no scripting language provides that. If you need performance, switch to a compiled language (or a jit-compiled interpreted language) that most of your co-workers understand.
If Python brings too many dependencies, I guess you're trying to do something that is way out of the realm of possibility for bash, so you're not looking for an "alternative" to bash, you're looking for the best language for whatever project you're undertaking. And you may not be able to get away from the dependencies if you're trying something specific/sophisticated.
Powershell is basically strongly typed.
> Can you be more specific than "fatigue"? That sounds like ennui
I love my job, but bash makes me wanna hang myself. I can say the same about stored procedures. Its like going back to cobol days. Some people have strong tastes I guess.
So doesn’t behave like a strongly typed language to me.
So yeah, PowerShell is huge on Windows and just awesome on Linux. You must also take into account that its only few years old in that space.
Simply untrue. See [1]. PS _may_ be superior _where it exists_, but without question it is available in far fewer places than bash.
[1]: https://github.com/PowerShell/PowerShell/issues/12612
Shell is likely to remain the best way to connect together other pieces of software.
The tipping point, in my experience, is folks trying to use arrays or other buffering of data. At that point they're writing in shell, but wishing for a procedural language.
Whereas, shell works excellently (and incredibly efficiently) if you can compose your task strictly into streams of data (a more 'functional' feel)
So the real answer I think is specific to the types of script you are writing!
But also it could be worth revisiting the style in which you're using shell; especially if you can call upon your own helper programs (eg. in Python) where you require buffering or non-streamed access to data.
Whilst there's a lot of documentation of sh/bash functionality, I don't think there's anything like the same momentum behind 'best practices' as other languages like Python or even C.
It's hard for us to help you with you giving us some context.
150-500 loc of bash are not that much, specially when you combine bash with standard Unix utilities.
These scripts you wrote, for what do you use them? Are they executed by humans or machines? On which platform? Linux, Mac, Windows or all?
You mentioned jq. Are you parsing json from HTTP requests or files? And why are you parsing json with bash?
Without a better context, A lot of people will say to use Python and Python is a good choice with you are running you scripts on Linux because you probably already have Python 3 installed and whatever you are writing with 500 loc of bash can be accomplished with zero dependencies with Python standard library.
If relying on the Python runtime is really a problem for you, your next best option is Go. Go is easy to learn and easy to use for building small cli apps with only the standard library, but even if you need some dependency, you will ship a single binary, so no problems with runtime compatibility or dependencies.
Consider docker for some situations. Consider kubernetes if the need is more devops/ running a production system.
Consider powershell if on windows.
Travis ci / Jenkins / appveyor etc. for CI
Be lazy - can I get out of writing this code should be in the forefront of your mind (for any code!). The answer might be no but consider it.
No code tools can be considered - Google sheets, Wordpress, plethora of saas to do this. Paas eg aws lambda etc.
So many options. Solve the problem, rather than transliterate bash.