Update: For examples of my coding approach, I’ve written solutions to a number of the exercism bash exercises that you can inspect. They won’t all make sense until you’ve gone through most of the series, but they’re a good resource to examine these techniques in action.
This is part one of a series on how to approach bash programming in a way that’s safer and more structured than your basic script.
I hope to pump out a number of these. They will be small in scope, but I have a lot to cover. There is a lot that can be done with bash if you adopt a number of strategies, and they add up.
By the end of the series, my goal is to outline enough techniques such that you can effectively build command-line utilities as well as small programs in bash.
In part one, I’ll discuss what bash is suitable for, as well as a couple of resources that can arm you with the proper information to be successful with it.
Bash - Huhh - Good God Y’all - What is it Good For?
There’s a justifiable love/hate relationship to be had with bash as a programming language. Bash’s warts will be the focus of the remainder of the series, so let’s look at its good points for the moment. To say it’s good for “absolutely nothing” would certainly be unfair.
Of course, there is plenty of competition from the likes of perl, python, ruby, zsh, powershell and other shell languages. I won’t spend much time comparing it to other languages…suffice to say that they all have their quirks, but most of them are better-designed than bash as general-purpose scripting languages. I’m not claiming otherwise.
So what are bash’s good points?
Bash is a very simple language. It has a few data types which are weakly-typed, which makes it simple to declare and use variables.
It is primarily oriented around strings as a data-type, which is convenient since it is mostly processing arguments and passing strings between external programs.
It makes some convenient container types available, such as arrays, which are dynamic, and associative arrays, which I will call hashes. These make handling sets of information with distinct elements easier.
It only has the basic control structures which you would expect from a programming language, such as for loops. If you know any other mainstream language, there are no surprises in the catalog. No object-oriented or functional programming tricks to learn here.
Bash has a limited set of builtin functionality which is extended by the ability to easily call external programs. There’s no standard library aside from what is builtin or distributed on most unix systems.
Bash doesn’t have a standard packaging system nor language support for modules. Nor does it have a standard logging system. While these are minuses for functionality, they’re also pluses for simplicity.
Simply put, bash is everywhere. The vast majority of unix distributions either come with bash as the default shell, come with it installed on the system, or have it available to install. Even MacOS and, now, Windows have system support for bash.
Only python 2 enjoys anywhere near that level of presence on systems everywhere, and not nearly so much as bash.
With a text file in the form of a bash script and nothing else required, I feel it’s fairly likely that bash provides the largest installed base of run-time environments in the world.
The weirdness of bash comes mostly from the fact that it is built to do one thing extremely well…call other command-line programs while staying out of your way.
If you want to control a unix system, there is no tool like the command-line. While many management tools are available for unix, none of them are as capable as the raw command-line. The cli is the first-supported interface for virtually any unix tool and also the best supported.
This makes bash the essential and standard tool for interacting with these other tools programmatically, especially when it is necessary to coordinate with more than one of them at a time. Python is sometimes referred to as a “glue” language, but bash embodies this idea better than python.
And as clunky as bash can seem, using other (non-shell) scripting languages to run a bunch of cli commands is far more clunky and probably a mistake.
Once you’ve learned a small set of bash’s operations, mostly string manipulation expressions, bash can allow you to be far more concise with your code than many other languages.
This is, of course, a double-edged sword, because conciseness can also mean opacity to the uninitiated. “Your code looks like line noise,” is a charge often leveled at languages such as perl, or bash in the hands of an expert.
I, however, view it as a major plus. While I still occasionally encounter head-scratchers in others’ code, I enjoy the fact that knowledge of a few standard idioms allows me to read more code in one screen of bash than in many other languages. It allows me to read and write quickly.
That’s about the size of it. I wish there were more reasons to love bash, because I actually enjoy programming with it quite a bit, although I am somewhat of a glutton for punishment.
Resources for Learning About Bash
As a quick aside, I would not recommend the freenode #bash channel. It’s not particularly friendly to newcomers to the language, and you’re definitely in for at least one lecture on why you shouldn’t be trying to do what you’re trying to do with bash.
I also would not recommend the Bashism wiki. It’s full of good information tinged with inaccuracies, bad opinions and outright bad advice. Bash is better than they make it out to be, and you deserve better too. There’s nothing there you can’t find elsewhere.
In general I would also say not to listen to anyone who tells you to not do this or that with bash. So long as you are trying to learn, no one should be warning you about how you will regret things or other such BS.
To be fair, I agree that you shouldn’t implement things in bash where it’s unwarranted, such as complicated programs for your day-job, or when someone else needs to maintain it. But if it’s for your own edification and won’t require others to get on the merry-go-round with you, then, by all means, full steam ahead.
The point of this series is to teach the mindset which avoids the pitfalls intimated by such ominous warnings in the first place, while enabling you to enjoy the experience of learning the numerous ins and outs of this challenging and, sometimes, rewarding language.
Now for the good stuff:
the bash hacker’s wiki - the best explanation of the details of bash’s operation
the google shell style guide - a reasonable starting point for learning about style, but take with a big grain of salt. It was written for Google employees, not the community.
one of the “awesome shell” lists - a jumping-off point for further exploration
Continue with part 2 - vim.
If you want to jump ahead to any specific topic, here’s a table of contents for the series:
- Part 1 - Intro
- Part 2 - Vim
- Part 3 - The Start
- Part 4 - Failure!
- Part 5 - Success!
- Part 6 - Outline Script
- Part 7 - Sourcing
- Part 8 - Support Library
- Part 9 - Another Test
- Part 10 - Test Independence
- Part 10.5 - Aside on Aliases
- Part 11 - Strict Mode
- Part 12 - Working in Strict Mode
- Part 13 - Implementing Strict Mode
- Part 14 - Updated Outline
- Part 15 - Strict Mode Caveats
- Part 16 - Recap
- Part 17 - Command Processing
- Part 18 - Word Splitting
- Part 19 - Disabling Word Splitting
- Part 19.5 - Disabling Path Expansion
- Part 20 - Scoping
- Part 21 - Environment Variables
- Part 22 - Data Types
- Part 22.5 - Naming and Namespaces
- Part 23 - Passing Arguments
- Part 24 - Passing Arrays
- Part 25 - Passing Hashes
- Part 26 - Returning Values
- Part 27 - Traps
- Part 28 - Tracebacks
- Part 29 - Debugging
- Part 30 - Option Parsing
- Part 31 - getopt
- Part 32 - Parallelism
- Part 33 - Modules
- Part 34 - Indirection
- Part 35 - Recursion
- Part 36 - Functional Programming