User Tools

Site Tools


ops102:bash_scripting

This is an old revision of the document!


NOTE: This page is being edited and is NOT ready for use.

Bash Scripting

What is a script?

A shell script is a computer program which is interpreted by an operating system shell.

Scipts are used to automate procedures that could be manually performed from the command line. They can potentially save a huge amount of time by eliminating repetitive commands. For example, if you're going to compile and test a program 100x, and each compilation and test cycle requires 25 steps (commands), you're looking at performing 2500 steps. It's much more efficient to create a script containing those 25 steps and run it as needed – in fact, you can even set things up so those commands execute automatically as soon as you save a new version of your program.

Basic Requirements for Shell Scripts

1. Create a text file containing shell commands. Use any text editor (nano, vi, VS Code, gnome-text-editor, eclipse, …) to create the file.

2. Tell the operating system which shell to use. Add a “shbang” line to the very top of the file, containing the text:

#!/usr/bin/bash

The first two chacters, the sharp (#) and bang (!) give this line its name. They are recognized by the operating system kernel as identifying a script. The remainder of this line is interpreted by the kernel as the name of the shell which is to be used to interpret the script. In this case, /usr/bin/bash is the absolute path of the bash shell. You can substitute other interpreters to write scripts in other shell dialects (such as the Z-shell, /usr/bin/zsh) or languages (such as python, /usr/bin/python).

Note that there must be nothing in font of the #! characters – no space and no blank lines.

3. Ensure that the script has appropriate permissions. The kernel requires execute [x] permission, and the shell requires read [r] permission. Set this up with the chmod command (for example, chmod u+rx scriptname).

Here is a simple example script using two commands, echo and date:

#!/usr/bin/bash
echo "The current date and time is:"
date

Notice the presence of the shbang line.

If this is save into the file named “now”, the permission could be set with this command:

$ chmod u+rx now

The script can then be executed. Normally, the current working directory is not searched, so to run the a script in the current directory, you will need to explicitly specify the directory name like this:

$ ./now
The current date and time is:
Sat Mar  6 12:03:32 EST 2038

Variables

Setting a Variable

To set a variable, simply type the variable name, an equal sign, and the variable value:

A=5
B=World

If the variable does not exist, it will be created. If it does exist, the previous value will be discarded.

Variable names may contain letters, digits, or underscores, but must not start with a digit.

Unlike some computer languages such as C, variables do not need to be declared. Variables are not typed – they may be used as strings, integers, or decimal values.

Accessing a Variable

To access a variable, place a dollar sign [$] in front of it, and use it in a command as an argument (or as a command name):

$ B=World
$ echo $B
World
$ echo Hello $B
Hello World

Quoting

Word Splitting and Quoting

Spaces and tabs are used to split a bash command line into individual “words”. This means that if you use an argument that contains a space, it will be treated as two separate arguments:

$ mkdir test
$ cd test           # this is new so it will be empty
$ touch new file
$ ls -l
total 0                                                                                                                 
-rw------- 1 chris.tyler users 0 Mar  6 12:12 file                                                                      
-rw------- 1 chris.tyler users 0 Mar  6 12:12 new    

Notice that touch new file created two files, because new and file were treated as separate arguments.

To prevent word splitting, quote the text.

$ mkdir test
$ cd test          # this directory is new so it will be empty
$ touch "new file"
$ ls -l 
total 0                                                                                                                 
-rw------- 1 chris.tyler users 0 Mar  6 12:15 new file 

Notice that only one file was created by the command touch “new file” because the quotes prevented word splitting, and the single argument new file was used as a single filename.

You'll also need to use quoting when assigning a string variable value containing a space:

$ A="Seneca Polytechnic"

You can quote text with single-quotes ['] or double-quotes [“]. Variable expansion takes place inside double quotes (this is called interpolation), but not inside single quotes:

$ B=World
$ echo "Hello $B"    # notice that $B is replaced with the value of B
Hello World
$ echo 'Hello $B'    # notice that $B used as-in
Hello $B

You should always double-quote variables that may contain a space in their value when using them as command arguments.

This is especially true for filenames – you never know when a user is going to put a space in a filename! Many scripts work find with opaque filenames (those containing no whitespace) but fail with non-opaque names.

Backslashes

A backslash [\] character outside of quotes or inside double quotes instructs the shell to ignore any special meaning that the following character may have. Examples:

$ touch "new file"
$ ls -l new\ file     # The space loses its special meaning as an argument separator
-rw-r--r--. 1 chris chris 0 Jun 18 22:49 'new file'

$ echo "This string contains a \"quoted\" string"
This string contains a "quoted" string

$ A=Testing
$ echo "  \$A"   # The dollar sign loses its special meaning
  $A

Environment Variables

By default, a variable is local to the shell in which it is running.

You can export variables to make them environment variables. That means that they are passed to programs executed by the shell.

Environment variables are used by all proccesses, not just the shell, and are commonly used to pass configuration information to programs.

To create an environment variable named X with a value of 999:

$ X=999
$ export X

Or in a single step:

$ export X=999

You can view all of the current variables (and shell functions) with the set command, or just the environment variables with the printenv command (you'll probably want to pipe the output through less).

The term environment variable is often shortened to the abbreviation envar.

Common Environment Variables

Environment Variable Purpose Examples
PS1 Normal (first-level) shell prompt PS1=“Enter command: “
PS1=”[\u@\h \W]\$ ”
EDITOR Path to the default text editor (typically /usr/bin/nano) EDITOR=/usr/bin/vi
PATH A colon-separated list of directories that will be searched when looking for a command PATH=“$PATH:.”
PATH=”/usr/bin:/usr/sbin:.“
LANG The default language – used to select message translations as well as number, currency, date, and calendar formats. LANG=en_CA.UTF-8
LANG=fr_CA.UTF-8
HOME The user's home directory - used for relative-to-home pathnames. HOME=/var/tmp
RANDOM A random integer (0-32767)

Reading Variable Values from Stdin: read

The read command reads a line of text from stdin and assigns it to the specified variable. For example, read A reads a line of text and assigns it to the variable A.

The read command can also send a prompt to stdout using the -p option:

$ read -p "Enter your name: " NAME
Enter your name: Chris
$ echo $NAME
Chris

Here is a script which uses a couple of read statements:

#!/usr/bin/bash
read -p "Please enter your name: " NAME
echo "Pleased to meet you, $NAME"
read -p "Please enter a filename: " FILE
echo "Saving your name into the file..."
echo "NAME=$NAME" >>$FILEecho "Done."

Command Capture

You can capture the output (stdout) of a command as a string using the notation $( ). and then use that string in a variable assignment or as a command argument:

$ echo "The current date and time is: $(date)"
The current date and time is: Mon 19 Jun 2034 12:02:11 AM EDT
$ FILES="$(ls|wc -l)"
$ echo "There are $FILES files in the current directory $(pwd)"
There are 2938 files in the current directory /bin

Avoid Backticks! You may see old scripts that use backticks (reverse single quotes) for command capture, like this:

$ A=`ls`

This is an archaic syntax with is depricated – avoid doing this. Some fonts make it hard to distiguish between backticks and single-quotes, and nesting backticks is difficult.

Arithmetic

Bash can perform integer arithmetic.

To evaluate an arithmetic expression and return a value, use $(( )):

$ A=100
$ B=12
$ echo $((A*B))
1200
$ echo $((B++))
12
$ echo $B

Note that inside the double-parenthesis, spaces don't matter, and it is not necessary to use a dollar-sign [$] in front of variables being accessed.

To evaluate an arithmetic expression without returning a value, use (( )):

$ A=100
$ B=13
$ ((A++))
$ echo $A
101
$ ((C=A*B*2))
$ echo "The answer is $C'
The answer is 2626

Exit Status Codes

When any process finishes executing, it exits with a numeric value. This can be called the exit code, status code, exit status code, or error code.

Usually, an exit status code of zero (0) means that no errors were encountered, and a non-zero code means that something went wrong. Therefore, it may be easiest to think of this as the error code, with 0 meaning no errors.

Be aware that program authors can use this value as they see fit, so the status code may indicate something else, such as the number of data items processed.

The special variable $? is set to the exit status code of the last command executed by the shell.

For example:

$ ls -d /etc
/etc
$ echo $?
0

$ ls -d /this/does/not/exist
ls: cannot access '/this/does/not/exist': No such file or directory
$ echo $?
2

Why is this important? Because exit status codes are the key to conditional logic (if…) and looping (for/while/until/…) in bash scripting.

Conditional Logic: if / then / elif / else / fi

Bash provides an if command to support conditional logic:

if LIST1 then

 LIST2

fi

If the command or commands in LIST1 execute successfully and return an exit status code of 0, then the commands in LIST2 are executed.

For example, you could use a grep command as LIST1 and an echo command as LIST2:

if grep -q "OPS102" courses.txt
then
  echo "The course code 'OPS102' was found in the file."
fi

The if command also supports elif (else if) and else keywords:

if grep -q "OPS102" courses.txt
then
  echo "The course code 'OPS102' was found in the file."
else
  echo "The course code 'OPS102' was NOT found in the file."
fi
if grep -q "OPS102" courses.txt
then
  echo "The course code 'OPS102' was found in the file."
elif grep -q "ULI101" courses.txt
  echo "The course code 'ULI101' was found in the file.
else
  echo "Neither 'OPS102' nor 'ULI101' was NOT found in the file."
fi
ops102/bash_scripting.1709833824.txt.gz · Last modified: 2024/04/16 18:10 (external edit)

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki