The Bash shell is definitely not the only shell out there, but it's one of the most powerful. This makes it a popular choice for systems administrators needing to develop serious applications that go beyond a simple "laundry list" of commands to run on a system. There are lots of great uses for other shells (I default to Tcsh for Git hooks, for instance), but Bash is an easy choice for serious scripting, and here's why.
Functions
To create a function in a Bash script, use the keyword function:
function foo {
# code here
}
Functions are useful for programmers because they help reduce redundancy in code. Less redundancy in code reduces the amount of maintenance required because if you need to change a value, you only have to find it once (in its function) rather than every time it's used.
How Bash stacks up
Functions are fairly well supported in shells, with the notable exception of Tcsh.
- Korn shell: Uses the same syntax as Bash
- Zsh: Uses the same syntax as Bash
- Fish: Uses a custom function command to create and manipulate functions
- Tcsh: Sometimes, you can work around the lack of functions in Tcsh by using the goto statement, but it's rarely the optimal choice.
Redirection
Redirection of input and output is a pretty standard function in any programming and scripting language, and it happens inherently when you're using a shell. You input text; you get output. Some shells are more robust than others, and as you might guess, Bash gives you nearly every option imaginable.
The basics are simple: use some number of > characters to redirect output and some number of < characters to redirect input. There are some special designations for specific types of input and output, though. For example, error messages go to a stream called stderr, which is designated as 2> for the purposes of redirection. For example, this command directs error messages to a file called output.log:
$ ls /void 2> output.log
How Bash stacks up
Redirection is a great convenience function when interacting with a shell, and while all major shells support some manner of redirection, not all features are available in all shells.
- Korn shell: Uses the same syntax as Bash
- Zsh: Uses the same syntax as Bash
- Fish: Partial support
- Tcsh: Partial support
Source
When you source a file in a shell, you're importing the file into your shell environment. Most shells handle this adeptly, but some provide a few extra features for your convenience.
For example, when you use source in Bash, it searches your current directory for the file you reference. If it can't find the file to source, it then searches your PATH instead. It's a small bonus, but it's so convenient because it allows you to store common functions in a centralized location on your drive and then treat your environment like an integrated development environment (IDE). You don't have to worry about where your functions are stored, because you know they're in your local equivalent of /usr/include, so no matter where you are when you source them, Bash finds them.
How Bash stacks up
Bash is the only shell that searches both the current directory and your PATH when you use either the source command or the abbreviated (and POSIX-ly correct) . notation.
Key bindings
Many shells allow you to customize how you interact with them, and of course, any interaction with a text-based interface begins with typing. In Bash, you can view your current keymap:
$ bind -V | grep keymap
To change your keymap (from Emacs to Vi, for example):
$ bind 'set keymap vi'
How Bash stacks up
Only Bash and Zsh provide built-in presets for key bindings.
History
Bash has the most robust command-history interface of any shell. The Bash version of history allows for reverse searches, quick recall, history editing (including deleting entries by line number), and more. All of the other shells combined match Bash's history interface, but none (even Zsh, which generally mimics Bash) match it on their own.
History manipulation
The history command isn't the only way to interact with your past. Bash has a rich collection of shorthand notation that enables you to recall and even modify commands in your shell history.
For instance, to run the most recent command again (the output of history | tail -n1, using a leading space, designated here by , to keep the history command out of your history), you can just type !! into Bash.:
$ wc -w luarocks.xml
1284 luarocks.xml
$ !!
1284 luarocks.xml
To run the most recent command while replacing part of that command with something else, you can use string substitution:
$ wc -w luarocks.xml
1284 luarocks.xml
$ ^-w^-l
$ wc -l luarocks.xml
214 luarocks.xml
There are many such shortcuts, all of them documented in the Bash man and info pages. Admittedly, many of these tricks are intended for advanced users to whom Bash interaction is so repetitive and mundane that obscure, seemingly random key combinations are useful. Zsh effectively mimics Bash, but no other shell offers this kind of maximized efficiency.
How Bash stacks up
Bash's history commands are unmatched by any other shell (Zsh comes close, but lacks some options, such as the ability to delete by line number).
Associative arrays
Most shells offer the ability to create, manipulate, and query indexed arrays. In plain English, an indexed array is a list of things prefixed with a number. This list of things, along with their assigned number, is conveniently wrapped up in a single variable, which makes it easy to "carry" it around in your code.
Bash, however, includes the ability to create associative arrays and treats these arrays the same as any other array. An associative array lets you create lists of key and value pairs, instead of just numbered values.
The nice thing about associative arrays is that keys can be arbitrary:
$ declare -A userdata
$ userdata[name]=seth
$ userdata[pass]=8eab07eb620533b083f241ec4e6b9724
$ userdata[login]=`date --utc +%s`
Query any key:
$ echo "${userdata[name]}"
seth
$ echo "${userdata[login]}"
1583362192
Most of the usual array operations you'd expect from an array are available.
How Bash stacks up
Bash is the only shell to provide full support for associative arrays (again, Zsh comes close but lacks functions to list keys).
Choose Bash for shell scripting
Some of the conveniences in Bash aren't POSIX-compliant. In theory, that means you could write a Bash script that doesn't run as expected on a system without Bash. If that happens, it's said that your script isn't "portable."
In practice, though, Bash is free and open source software, so anyone can install it, whether they run Linux, BSD, OpenIndiana, Windows, or macOS. Installing Bash doesn't require the user to use Bash as their default shell, or even to consciously launch it, because a good shell script identifies what shell it uses in its top shebang line (#!/bin/bash, for example).
When in doubt, include Bash as a dependency of your shell script (even if it's the only dependency) in your documentation to alert users that it's a Bash script and not a generic shell script.
Bash has many conveniences, and I find that those outweigh concerns over whether or not a user has installed Bash. Just as with Python or Java or any other software, sometimes there are dependencies. If you enjoy Bash and find its shorthand and shortcuts useful, don't shortchange yourself.
Comments are closed.