Skip to content
Justin Too edited this page Nov 12, 2016 · 11 revisions

Why Bash?

The short answer is because linux is really GNU/Linux. Only the kernel is linux but the base collection of utilities providing the Unix like environment is provided by GNU and the GNU shell is bash

unix.stackexchange.com: Why is bash standard on Linux?

Current Bash Version

$ echo $BASH_VERSION

See Stackoverflow.com: How can I get iTerm to use the newer version of bash that brew shows? Change a user's shell on OSX:

bash --version (or bash -version) will NOT report the CURRENT shell's version, but the version of the bash executable that comes FIRST IN THE $PATH.

Double Colons (::) in Function Names

See owigger-snippets.blogspot.com: double colons in Bourne Shell function names:

Bourne shell does not like double colons ("::") in identifiers. Other shells, e.g. bash, have no problems with them, and programmers like to use them for generating a pseudo-namespace look.

A problem arises when I want to write an include file that works for both, bash and sh.

Here is a workaround that lets me define function names in Bourne Shell with double colons in their name:

function foo__bar() {
 ...
}

alias foo::bar=foo__bar

Variables

Default Values

# Variable $foo has not been set. In strict mode,
# next line triggers an error.
bar=$foo

# ${VARNAME:-DEFAULT_VALUE} evals to DEFAULT_VALUE if VARNAME undefined.
# So here, $bar is set to "alpha":
bar=${foo:-alpha}

Error Handling

Exception Handling - try/catch

{ # try

    command1

} || { # catch
    # save log for exception 
}

Arrays

#!/bin/bash
names=(
  "Aaron Maxwell"
  "Wayne Gretzky"
  "David Beckham"
  "Anderson da Silva"
)

echo "With default IFS value..."
for name in ${names[@]}; do
  echo "$name"
done

echo ""
echo "With strict-mode IFS value..."
IFS=$'\n\t'
for name in ${names[@]}; do
  echo "$name"
done

Here's the output:

With default IFS value...
Aaron
Maxwell
Wayne
Gretzky
David
Beckham
Anderson
da
Silva

With strict-mode IFS value...
Aaron Maxwell
Wayne Gretzky
David Beckham
Anderson da Silva

See Use the Unofficial Bash Strict Mode (Unless You Looove Debugging).

Error Handling

Handle an error

#!/bin/bash
set -o errexit ; set -o nounset

bad_command || do_err_handle
good_command

See stackoverflow.com: proper way to detect shell exit code when errexit option set.

Force Pass

# "grep -c" reports the number of matching lines. If the number is 0,
# then grep's exit status is 1, but we don't care - we just want to
# know the number of matches, even if that number is zero.

# Under strict mode, the next line aborts with an error:
count=$(grep -c some-string some-file)

# But this one behaves more nicely:
count=$(grep -c some-string some-file || true)

See Use the Unofficial Bash Strict Mode (Unless You Looove Debugging).

Obtain exit code

Temporarily disable errexit:

set +e
count=$(grep -c some-string some-file)
retval=$?
set -e

See Use the Unofficial Bash Strict Mode (Unless You Looove Debugging).

Command Chains

Fail script if second_task fails:

first_task && {
    second_task
    third_task
}
next_task

The following will not work as you might expect/plan:

first_task && second_task && third_task
# And more lines of code following:
next_task

The potential problem: if second_task fails, third_task will not run, and execution will continue to the next line of code - next_task, in this example. This may be exactly the behavior you want. Alternatively, you may be intending that if second_task fails, the script should immediately exit with its error code.

See Use the Unofficial Bash Strict Mode (Unless You Looove Debugging).

Resources