I’ve written some bash over the years. I’m a big fan of building out robust .bash_profiles, but I’ve always just “hacked” on Bash. I’d try writing some logic three or four[1] times until I found the syntax that “worked.” I’ve read tutorials, documentation, and blog posts on bash, but I’ve never “grokked it.”
Then, at MidwestPHP 2018 I got to attend a talk titled “BASHing at the CLI.” It was given by Chris Tankersley, @dragonmantank on Twitter.
I'm learning more about coding in BASH in @dragonmantank 's "BASHing at the CLI" talk at #mwphp18 than in several years of studying it on my own. O.o
— Wyatt Andersen (@wandersen02) March 9, 2018
Very cool. pic.twitter.com/FmXh5FcXfu
That was a great talk. I learned a lot, and immediately after I sat down at my Mac to work through the things I learned.
What follows is a poor substitute for Tankersley’s talk (and his expertise), but I wanted to get it written down, if only to help me absorb.
MidwestPHP is a great conference. If you have any interest in PHP, and live, or want to travel to the “midwest” (or the “North” as I think we’re supposed to call it now…) - you should come!
I’m one of the organizers, and have been for years, so I’m maybe a little bias. But… Still:
Go to MidwestPHP.
“Bash is the GNU Project’s shell. Bash is the Bourne Again SHell. Bash is an sh-compatible shell that incorporates useful features from the Korn shell (ksh) and C shell (csh). It is intended to conform to the IEEE POSIX P1003.2/ISO 9945.2 Shell and Tools standard. It offers functional improvements over sh for both programming and interactive use. In addition, most sh scripts can be run by Bash without modification.” - GNU
Bash is a low level language that interfaces directly with other Unix utilities. It’s commonly used in ops because most Unix systems can run it without having to install any external dependancies. It’s also commonly used in .bash_profiles to construct utilities like:
#!/usr/bash
rebase () {
git checkout master
git pull origin master
git checkout $1
git pull --rebase origin master
git rebase -i `git merge-base $1 master`
}
A bash script start’s with a shebang, and the path to the bash executable you mean to use:
#!/usr/bash
An if
statement ends with fi
, a case
statement ends with esac
(“if” and “case” backwards). But a for
loop ends with done
not rof
.
foo="bar"
is valid Bash. foo = "bar"
will not do what you think it will:
#!/usr/bash
foo = "bar"
...
> # terminal
> bash foo.sh
foo.sh: line 2: foo: command not found
Spaces also matter in if
statements. if [ 0 -eq 1 ]; then
is valid Bash, if [0 -eq 1]; then
will not work like you think it will:
#!/usr/bash
if [0 -eq 1]; then
echo "bar"
fi
...
> bash foo.sh
foo.sh: line 2: [0: command not found
Variables are global unless explicitly set to be local
.
#!/bin/bash
function foo()
{
BAR="baz"
}
foo
function fizz()
{
local BUZZ="fizzbuzz"
}
foo
...
> bash foo.sh
baz
# ^ note the empty line above where "fizzbuzz" isn't
You don’t get to do function foo(variable_1, variable_2)
in Bash. Your function can still take parameters, just not with a method signature.
#!/bin/bash
function foo()
{
echo "${1}"
}
foo Hello World
foo "Hello World"
...
> # terminal
> bash foo.sh
Hello
Hello World
String comparison:
if [ "foo" == "bar" ] # Equal to
if [ "foo" != "bar" ] # Not equal to
Integer comparison:
if [ 1 -eq 0 ] # Equal to
if [ 1 -ne 0 ] # Not equal to
if [ 1 -gt 0 ] # Greater than
if [ 1 -ge 0 ] # Greater than or equal to
if [ 1 -lt 0 ] # Less than
if [ 1 -le 0 ] # Less than or equal to
All bash scripts should exit
with a integer. By convention and expectation 0
is “success” - 200 OK
-esque.
exit 0
You may define any other integer to convey other meanings as appropriate, exit 1
is a conventional “error” - 500 Error
-esque.
The following script prints “Hello World” using a variety of methods available in Bash.
Tankersley mentioned the Google Bash Style Guide. I’ve read through it and tried it out a bit, but I haven’t used it enough to recommend one way or another, but there it is.