PROGRAMMING LANGUAGES

Bash

Date Published:
Last Modified:

Overview

Bash, apart from being a UNIX shell, is also a programming language in it's own right. It is commonly referred to as a scripting language because the script is not compiled, but rather interpreted at runtime.

Shebang

A shebang at the top of a Bash script allows you to run the script directly from the command-line as if it was an executable, e.g. by calling ./my_script.sh. This is because the shebang tells your shell (which may or may not be Bash) to call the mentioned program, and pass in the script as the first parameter.

The recommended shebang to use at the top of a bash script is:

#!/usr/bin/env bash

## Make sure ^^^ is the first line in your bash script!!!

The above variant gives the greatest portability, as different UNIX systems put the bash program at different locations on the system (e.g. OpenBSD and FreeBSD both do not have bash at /bin/bash). The above shebang is an instruction to run the first bash that is found on the PATH.

A less optimal bash shebang is:

#!/bin/bash

This is not as portable as the bash location is hard-coded into the shebang.

Finally, it is not recommended to use the shebang:

#! /bin/sh

if you want to run bash, as /bin/sh is usually a symlink to the operating systems default shell, which may or may not be bash (it could also be dash).

Returning From Scripts

The return statement can be used to return from a function within a script. return has nothing to do with the exit code.

The exit code of the last command run in a script is used as the exit code that is returned to the parent program.

You can also use the command exit <number> at any time to return from the script.

exit 0 # Return indicating success
exit 1 # Return indicating generic failure

ANSI Escape Codes

Bash supports various ANSI escape codes. This includes the commonly used codes to colour text printed to the terminal.

Getting The Current Script Directory

You can read the current script directory as a string into a variable using the following code:

script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

Command-line Argument Parsing

Command-line argument parsing in bash is notoriously tricky.

Thankfully, some open-source third party libraries come to the rescue. One of the most powerful command-line argument parsing libraries for shell scripts is shflags, a port of the Google gflags library for the UNIX shell.

The simple Hello, World example (taken from their website) is shown below. If you create a file called hello_world.sh with the following contents:

#!/bin/sh

# source shflags
. /path/to/shflags

# define a 'name' command-line string flag
DEFINE_string 'name' 'world' 'name to say hello to' 'n'

# parse the command-line
FLAGS "$@" || exit $?
eval set -- "${FLAGS_ARGV}"

# say Hello!
echo "Hello, ${FLAGS_name}!"

You could then call this as so:

$ ./hello_world.sh
Hello, world!

$ ./hello_world.sh -n Eugene
Hello, Eugene!

$ ./hello_world.sh -h
USAGE: ./hello_world.sh [flags] args
flags:
    -h  show this help
    -n  name to say hello to

As shown above, it also supports automatic help! Although the shebang in the above example is #!/bin/sh, this can be changed to #!/usr/bin/env bash without any issues.

Refreshing The Current Working Directory After It Was Deleted And Recreated

If the directory you are currently in during a bash session gets deleted and recreated, you may notice that commands like ls don't work. That is because the directory you were in no longer exists, the new directory is a different directory (different inode) but shares the same name.

To quickly get back to a valid state of the working directory, type:

cd `pwd`

The works by re-entering a directory with the same path as the old one you are currently in.

Command Substitution

You can use ` (backticks) or $() for command substitution. For example:

A=`ls .`

The above command will store the output of the command ls . (which will be a list of the contents of the current directory) into the variable A. It will not print the output of ls to the console (as it usually would). Now if we went:

echo $A
# <contents of dir will be printed here>

It is recommended that you use the $() syntax rather than `, as $() is standardized by POSIX and should work for most shells.

Dealing With Special Characters In Filenames And Directories

You can run into issues when trying to create, copy, move, delete or otherwise manipulate files and directories in bash when the names contain special characters such as -, $, # and ; (which are all allowed to be included in file names and directory names as per the UNIX specification).

The following sections show you how to deal with these special characters.

Hyphens

File names which start with hyphens can be troublesome to manage. Most programs (note: not the shell, but the program) will interpret the hyphen as a option specifier and try and parse the start of the filename as an option.

There are two common ways to deal with this problem. The first is to use the -- specifier. For most common utility programs (e.g. touch, mv, cp, rm) everything after a -- is not parsed for options:

$ rm -rf -- -file_starting_with_hyphen.txt

or you explicitly include the current directory in the path:

$ rm -rf ./*

Hashes

Hashes cannot be dealt with in the same way as hyphens, because they are interpreted differently by the shell (e.g. bash), not the program. The -- method will not work. However, you can prefix the file with the current directory:

$ touch ./#file.txt

You can enclose the filename in ' quotes, which tell bash not to interpret the #:

$ rm '#file.txt'

Semicolons

Bash uses semicolons to delimit separate commands, and so you have to enclose filenames and directories in single quotes ('), which will tell Bash not to interpret the ; as a command separator:

touch ';test.txt'

Like this page? Upvote with shurikens!

Related Content:

Tags:

comments powered by Disqus