blakehawkins.com

An ode to bash

Posted 2020-10-18

I like bash, and I somehow find that surprising. Bash is dangerous, bash is old, bash is simple. Other people - people I respect - like bash too.

Why not?

Across all of the awful things that bash does wrong - that make bash a bad programming language - my biggest complaint is the general idea that bash is unsafe. It is unclear when - if at all - types are meaningful in bash. Code is highly imperative. Error-handling is almost non-existant. Global state 😱. This all normally spells doom.

Aside: why so?

I often think of programming languages on the imperative <-> functional axis. I like the languages on the right, and dislike the ones on the left.

The obvious counter is that sometimes you need something quick and dirty, something that doesn't need to compile, something that doesn't need expensive virtual machine spin-up.

We are sort of left then with bash, perl, php, python, and ruby for quick and dirty. And, to be fair, I could imagine using irb as a shell environment. The others: I could not.

Bash has something else going for it: it's ubiquitous.

Supposing for a moment that ubiquity is a sufficient argument to justify learning some bash, I imagine I haven't convinced you: people seem to settle on a primary language, a scripting language, and some basic bash.

I'm here to tell you that you should just use bash as your scripting language, and any time you need "real" scripting capabilities, you really just needed your primary language, and if your primary language seemed too clunky, that's a problem in itself.

I will die on this hill: python is garbage. Just use bash.

Something something monads

Bash has something more going for it.

When we write scripting languages like python, we normally utilise imperative state, and some nice higher level constructs like list comprehensions, to transform some data.

I'll argue then that python's primary interface is the collection. Python code is often all about manipulating collections.

Thinking about primary languages, I'll mention a couple.

Rust - my primary language - has a familiar ruby- or java-like syntax (a lot of methods) that many are familiar with. But - in terms of thinking about writing code and decomposing problems, Rust's biggest win is its powerful enums and traits.

So I'll argue that rust's primary interface is the trait, which when wielded, categorises the world and declaratively describes the world's capabilities.

Java has baggage (DI, code-generation, gradle, annotation-hell), but its claim to fame is the Object.

Java's primary interface is in mapping the relationship between different kinds of data, and things that manipulate data.

Bash has a primary interface too!

I/O

Bash's primary interface - and the thing that makes bash powerful, productive, and admired by all - is the fact that everything is built around I/O.

In that sense, bash is hardly even a real language. Bash just leverages subprocesses, and gives you fine control over where to wire up signals.

That's what makes bash elegant, but it's also what makes bash incredibly powerful, not only for composing systems, but for decomposing software architecture into its critical signals.

One complaint about bash seems to be that everyone writes their own flavor of it.

My flavor is one big pipeline, with some basic safety sprinked in.

Example

I have a small bit of software on a cron-job which scrapes property listings online and writes a summary to a matrix.org channel.

To write this software I decomposed the problem into pure I/O and only wrote the applications I needed to represent that problem space as a pipeline (ref: 1, 2, 3, 4).

This pattern is a beaut, and one I'd like to repeat more of in the future.

I've heard folks refer to this as "using bash as glue code", but this seems to be taking things one step further.

TL;DR

My flavor of bash is better than yours, because it elevates my primary language, minimises bash's safety impact, helps me decompose my software, and gives me a topic for a blog post.