Features

Bash is a Turing-complete language and provides a large variety of features.


Some of them are common to most popular programming languages:

  • variables
  • conditional statements
  • loops
  • functions
  • IO & file operations
  • etc

Others are specific to bash and the terminal environment:

  • pipes
  • job controls
  • shell expansions
  • special variables
  • command history
  • etc

This course aims to provide you with working knowledge of bash and the Unix ecosystem, but CANNOT cover all the topics in detail


Operational modes

Bash can be used either:

  • in interactive mode: commands are executed as soon as the user hits enter.

  • for scripts: they are executed to completion and can accept variable arguments


Interactive mode

This is an example of a minimal prompt.

$

It signifies that the shell is ready to accept commands.

The prompt can be customized to include any desirable information such as:

  • the current directory
  • the machine name
  • the current user
  • the git branch
  • the exit code of the last command
  • and more

in the examples we will exclude the prompt in order to simplify the copy/pasting of the sample commands


Hello world

# a hash indicates a comment, anything after it is ignored

echo 'Hello World!'

Once the user submits the command, it executes, and prints the output to the screen.

The echo command prints the provided argument to standard out


Beware: Bash is case-sensitive, so trying

EcHo 'hello world'

might NOT produce the expected result


Anatomy of a command

All commands in bash follow the following general structure:

[variables] [command] [options] [arguments] [redirects] [operators]

The command is the only mandatory element, everything else is optional

Command parsing order

Bash processes commands in this order:

  1. History expansion (if enabled)
  2. Quote removal and word splitting
  3. Expansions (brace, tilde, parameter, arithmetic, command substitution, pathname)
  4. Redirection setup
  5. Command execution

Output

There are 2 main output streams:

  • standard output (stdout) for regular program output

  • standard error (stderr) for error messages and diagnostics.

By default, both streams are displayed on the terminal.


Command types

There are several types of commands in bash.

When trying to execute a command bash will search for it in this order:

  • aliases
  • keywords
  • functions
  • built-ins
  • external programs (resolved through $PATH)

Use the type built-in command to check the type of another command.

type grep
# prints: grep is /usr/bin/grep

type if
# prints: if is a reserved word

type cd
# prints: cd is a shell builtin

type ll
# prints: ll is an alias for ls -alh

Built-ins and keywords

Use the help command to get basic information about built-ins and keywords.

help # prints all built-ins and keywords

help if # prints information about the if keyword

Aliases

Use the alias command to view or define aliases.

alias # prints all aliases

alias ll='ls -alh' # define a new alias

NOTE: there must be NO spaces around the = sign. Also, unlike other languages, bash handles single and double quotes differently.


Executing specific command variant

# a bit of setup
alias echo='echo "ALIASED:"'

echo "test"
# uses alias, prints: ALIASED: test

builtin echo "test"
# uses built-in, prints: test

command echo "test"
# uses external, prints: test

Options and arguments

Each command may accept any number of options and/or arguments.

  • Options usually start with a - and modify the behaviour of the command.

  • Arguments represent data to be used be the command


For example, the -t option makes the type command return a single word output

type grep
# prints: grep is /usr/bin/grep

type -t grep
# prints: file

Usually options precede arguments


Exit status

Because bash prints both standard output and errors to the terminal, it might be hard to determine whether a command succeeded.

Fortunately, bash provides a special variable $? to indicate the exit status of the last command.


A value of 0 means success. Any other (positive number) indicates an error.

type ls
echo $?
# prints 0

type xxx-no-such-command
echo $?
# prints 1

Which is NOT a valid command type?

  • built-in
  • file (external)
  • interactive
  • alias
  • function

Variables

  • Bash provides a large number of built-in variables.

  • External programs and the operating system also expose a lot of variables.

  • You can define your own variables and expose them to the system.


Use the env command to see all variables defined on the system.

Or echo to print the value of a specific variable:

echo $BASH_VERSION
# prints: bash-5.3

NOTICE the $ character used to reference the variables


Special variables

There are many pre-defined and built-in variables depending on the specific environment. Some useful ones are:

$TERM # the name / type of the terminal
$SHELL # the name of the shell (e.g bash)
$PATH # : separated list of directory
$EDITOR # default text editor
$USERNAME # the login name of the current user

Defining variables

To define your own variables use the NAME=value syntax.

You can define multiple variables on the same line.

MY_VAR='hello' fruit='banana'

echo $fruit $MY_VAR
# prints: banana hello

NOTICE there is NO $ character when defining variables.

While it is NOT required for variables to be in ALL CAPS, it is a common convention, especially for global variables.


Variable scope

By default variables are scoped to the current shell.

Use the export keyword to make a variable global:

export MY_COLOR='green'
export MY_THEME='dark'
export MY_FONT='Plex Mono'

One variable, multiple terminals

Define a normal variable in one terminal window and try printing it in another. Export a variable and trying printing it in another window as well.


Variables can also be scoped to a specific command:

date
# prints: current date in local time zone

TZ=UTC date
# prints: current date in UTC

echo $TZ
# prints nothing, TZ was only defined for that command

The $ character is used for variable expansion.

It allows multiple advanced features:

my_var='abcdef'
echo ${my_var}
# same as echo $my_var

echo ${#my_var}
# prints: 6 - the length of the value

echo ${my_var^^}
# prints: ABCDEF - all uppercase

echo ${my_var:1:3}
# prints: bcd - subset of characters

Single vs double quotes

Single quotes denote a literal string, while double quotes allow for variable expansion.

echo '$MY_VAR test'
# prints: $MY_VAR test

echo "$MY_VAR test"
# prints: hello test

It is recommended to always use quotes.

While bash allows unquoted values, this may lead to hard-to-debug problems.


Subshells

By default, the output of a command goes to stdout. But there are ways to redirect or capture the result of a command:

today=date
echo $today
# doesn't work.. prints the literal 'date'

today=$(date)
echo $today
# works! prints today's date

Subshells can also be used to pass the output of one command as an argument to another:

du -h $(which ls)
# print: 40K /bin/ls
  • du returns the size of a file
  • which is similar to type, but resolves $PATH first

Pipes

Another way of redirecting the output of a command is by using a pipe |:

env | grep MY_
# prints all global variables that contain the string MY_

Using a pipe we are redirecting the output of the first command to the stdin of the second.

Customize your prompt

Use bash to customize your prompt. Explore options for what information would be most useful to you. Play around with fun colors and special symbols.

Hint: to modify your prompt set a value to the PS1 special variable.