This may make some people pull their hair out, but I’d love to hear some arguments. I’ve had the impression that people really don’t like bash, not from here, but just from people I’ve worked with.
There was a task at work where we wanted something that’ll run on a regular basis, and doesn’t do anything complex aside from reading from the database and sending the output to some web API. Pretty common these days.
I can’t think of a simpler scripting language to use than bash. Here are my reasons:
- Reading from the environment is easy, and so is falling back to some value; just do
${VAR:-fallback}
; no need to write another if-statement to check for nullity. Wanna check if a variable’s set to something expected?if [[ <test goes here> ]]; then <handle>; fi
- Reading from arguments is also straightforward; instead of a
import os; os.args[1]
in Python, you just do$1
. - Sending a file via HTTP as part of an
application/x-www-form-urlencoded
request is super easy withcurl
. In most programming languages, you’d have to manually open the file, read them into bytes, before putting it into your request for the http library that you need to import.curl
already does all that. - Need to read from a
curl
response and it’s JSON? Reach forjq
. - Instead of having to set up a connection object/instance to your database, give
sqlite
,psql
,duckdb
or whichever cli db client a connection string with your query and be on your way. - Shipping is… fairly easy? Especially if docker is common in your infrastructure. Pull
Ubuntu
ordebian
oralpine
, install your dependencies through the package manager, and you’re good to go. If you stay within Linux and don’t have to deal with differences in bash and core utilities between different OSes (looking at you macOS), and assuming you tried to not to do anything too crazy and bring in necessary dependencies in the form of calling them, it should be fairly portable.
Sure, there can be security vulnerability concerns, but you’d still have to deal with the same problems with your Pythons your Rubies etc.
For most bash gotchas, shellcheck
does a great job at warning you about them, and telling how to address those gotchas.
There are probably a bunch of other considerations but I can’t think of them off the top of my head, but I’ve addressed a bunch before.
So what’s the dealeo? What am I missing that may not actually be addressable?
I'm afraid your colleagues are completely right and you are wrong, but it sounds like you genuinely are curious so I'll try to answer.
I think the fundamental thing you're forgetting is robustness. Yes Bash is convenient for making something that works once, in the same way that duct tape is convenient for fixes that work for a bit. But for production use you want something reliable and robust that is going to work all the time.
I suspect you just haven't used Bash enough to hit some of the many many footguns. Or maybe when you did hit them you thought "oops I made a mistake", rather than "this is dumb; I wouldn't have had this issue in a proper programming language".
The main footguns are:
shellcheck
. I have too. That's not a criticism. It's basically impossible to get quoting completely right in any vaguely complex Bash script.set -e
, but then that breaks pipelines and conditionals, and you end up with really monstrous pipelines full ofpipefail
noise. It's also extremely easy to forgetset -e
.No. If it's missing
$1
will silently become an empty string.os.args[1]
will throw an error. Much more robust.Absolutely not. Python is strongly typed, and even statically typed if you want. Light years ahead of Bash's mess. Quoting is pretty easy to get right in Python.
I actually started keeping a list of bugs at work that were caused directly by people using Bash. I'll dig it out tomorrow and give you some real world examples.
Agreed.
Also gtfobins is a great resource in addition to shellcheck to try to make secure scripts.
For instance I felt upon a script like this recently:
Quotes are OK, shellcheck is happy, but, according to gtfobins, you can abuse tar, so running the script like this:
./test.sh /dev/null --checkpoint=1 --checkpoint-action=exec=/bin/sh
ends up spawning an interactive shell...So you can add up binaries insanity on top of bash's mess.
Meh, most in that list are just "if it has the SUID bit set, it can be used to break out of your security context".