bash Shell Scripting
Overview
Here is a basic bash shell script:
#!/bin/bash echo "Hello World"The first line tells Linux to use the bash interpreter to run this script. To determine where your bash interpreter is located, run:
which bashAfter saving the above script to hello.sh, to make it executable, we would run something like:
if ... else ... elif ... fi
The following checks for the existence of a file:
#!/bin/bash if [ -f /etc/foo ] then cp /etc/foo /tmp echo "Done." else echo "This file does not exist." exit fiHere are some file checking options:
Option Check if file... -d ...is a directory -e ...exists -f ...is a regular file -g ...has SGID permissions -r ...is readable -s ...has size > 0 -u ...has SUID permissions -w ...is writable -x ...is Executable
while ... do ... done
The while structure is a looping structure. While condition is true, do.#!/bin/bash while true do echo "Press CTRL-C to quit." doneHere is an alternative:
#!/bin/bash while : do echo "Press CTRL-C to quit." doneThe latter is faster because it is a built in in bash.
To check the condition of of a variable:
#!/bin/bash x=0 while [ "$x" -le 10 ] do echo "Current value of x: $x" x=$(expr $x + 1) sleep 1 done$(...) is a way of telling the shell that you want to run the command expr $x + 1, and assign its result to x. Any command enclosed in $(...) will be run:
#!/bin/bash me=$(whoami) echo "I am $me."
Check equality between numbers:
x -eq y x is equals to y x -ne y x is not equals to y x -gt y x is greater than y x -lt y x is less than y
Check equality between strings:
x = y x is the same as y x != y x is not the same as y -n x Evaluates to true if x is not null -z x Evaluates to true if x is null.
until ... do ... done
The while structure loops while the condition is true. The until structure loops until the condition is true.#!/bin/bash x=0 until [ "$x" -ge 10 ] do echo "Current value of x: $x" x=$(expr $x + 1) sleep 1 done
for ... in ... do ... done
The for structure will loop through a range of variables.#!/bin/bash echo -n "Checking system for errors" for dots in 1 2 3 4 5 6 7 8 9 10 do echo -n "." done echo "System clean."The -n option prevents a new line from automatically being added.
#!/bin/bash for x in muon gluon meson do echo "The value of variable x is: $x" sleep 1 doneThe following adds a .html extension to all files in the current directory:
#!/bin/bash for file in * do echo "Adding .html extension to $file..." mv $file $file.html sleep 1 doneThe splat (*) is a wild card character that will, in this case, match every file in the current directory.
case ... in ... esac
The case structure is useful for times where there are a lot of conditions to be checked, and you do not want to have to use if over and over again.#!/bin/bash x=5 case $x in 0) echo "Value of x is 0." ;; 5) echo "Value of x is 5." ;; 9) echo "Value of x is 9." ;; *) echo "Unrecognized value." esacThe case structure will check the value of x against 3 possibilities. If all the checks fail, it will produce a message.
Quotes
There are three types of quotation marks:
Double " " Single ' ' Back ` ` Double quotes are used mainly to hold strings of words and whitespace. A string enclosed in double quotes is treated as one argument. For example...
...versus...> mkdir hello world > ls -F hello/ world/> mkdir "hello world" > ls -F hello/ hello world/ world/If a variable is enclosed in double quotes, its value will be evaluated. If it is enclosed in single quotes, its value will not be evaluated.
#!/bin/bash x=5 echo "Using double quotes, the value of x is: $x" echo 'Using single quotes, the value of x is: $x'Single quotes can be used to preserve whitespace just like double quotes:
> mkdir 'hello world' > ls -F hello world/
Back quotes, also called back tics, are completely different from double and single quotes, and are generally used to evaluate expressions. For example:
x=`expr $x + 1`
...and...
$!/bin/bash echo "I am `whoami`"
Math With Bash
The fastest way to do math is to use bash's built-in shell feature#!/bin/bash x=8 y=4 z=$(($x + $y)) echo "The sum of $x + $y is $z"bash has the following math operators:
ACTION OPERATOR Addition + Subtraction - Multiplication * Division / Modulus % For example:
#!/bin/bash x=5 y=3 add=$(($x + $y)) sub=$(($x - $y)) mul=$(($x * $y)) div=$(($x / $y)) mod=$(($x % $y)) echo "Sum: $add" echo "Difference: $sub" echo "Product: $mul" echo "Quotient: $div" echo "Remainder: $mod"The above can be written using expr:
...and...add=$(expr $x + $y)add=`expr $x + $y`.
User Input
To get input from a user:#!/bin/bash echo -n "Enter your name: " read user_name echo "Hello $user_name!"The read function stores all input into a variable until the user the <Enter> key.
#!/bin/bash echo -n "Enter your name: " read user_name if [ -z "$user_name" ] then echo "You did not tell me your name!" exit fiecho "Hello $user_name!"
Functions
Functions break up a program into smaller pieces.#!/bin/bash hello() { echo "Inside function hello()" } echo "Entering function hello()" hello echo "Outside of function hello()"Functions should always be defined before they are called. The following is incorrect:
#!/bin/bash echo "Entering function hello()..." hello echo "Outside of function hello()" hello() { echo "Inside function hello()" }Always have your functions at the start of your code, or at least, before you call the function.
Here is a more sophisticated example:
#!/bin/bash new_user() { echo "Preparing to add a new user..." sleep 2 adduser } echo "1. Add user" echo "2. Exit" echo "Enter your choice: " read choice case $choice in 1) new_user ;; *) exit ;; esac
Trapping
The built in trap command is used to gracefully exit programs. For instance, if you have a program running, hitting CTRL-C will send the program an interrupt signal, which will kill the program. trap uses the following syntax:trap action signalaction is what you want to do when the signal is activated, and signal is the signal to look for.
A list of signals can be found by running trap -l.
When using signals in your shell programs, omit the first three letters of the signal, usually SIG. For instance, the interrupt signal is SIGINT. In your shell programs, just use INT. You can also use the signal number that comes beside the signal name. For instance, the numerical signal value of SIGINT is 2. Try out the following program:
#!/bin/bash trap sorry INT sorry() { echo "I'm sorry Dave. I can't do that." sleep 3 } for i in 10 9 8 7 6 5 4 3 2 1 do echo $i seconds until system failure." sleep 1 done echo "System failure."You can have trap ignore the signal by having "''" in place of the action. To reset a trap to it's original value, use a dash: "-" in place of the action.
trap sorry INT trap - INT trap '' INTWhen a trap is reset, it defaults to its original action, which is, to interrupt the program and kill it. When you set it to do nothing, it does just that. Nothing. The program will continue to run, ignoring the signal.
And & Or
The AND statement first checks the leftmost condition. If it is true, then it checks the second condition. If it is true, then the rest of the code is executed. If condition_1 returns false, then condition_2 will not be executed.
#!/bin/bash x=5 y=10 if [ "$x" -eq 5 ] && [ "$y" -eq 10 ] then echo "Both conditions are true." else echo "The conditions are not true." fiHere, we find that x and y both hold the values we are checking for, and so the conditions are true.
The with an OR statement subsequent code will be executed provided at least one of the tested conditions is true:
#!/bin/bash x=3 y=2 if [ "$x" -eq 5 ] || [ "$y" -eq 2 ] then echo "One of the conditions is true." echo "None of the conditions are true." fi
Arguments and Parameters
The $# variable stands for the total number of arguments passed to the program. For instance, if you run a program as follows:foo argument$# would have a value of one, because there is only one argument passed to the program. If you have two arguments, then $# would have a value of two.
Each word on the command line to as variables within the shell program. foo would be $0. argument would be $1. You can have up to 9 variables.
#!/bin/bash if [ "$#" -ne 1 ] then echo "usage: $0 <argument>" fiecho "The argument is $1"
$* represents all of the arguments on the command line.
Redirection AND Piping
Standard output (stdout) is printed to the screen. For instance:echo "Hello World" Hello WorldRedirection allows you to redirect the output somewhere else, such as a file or a tape drive.
echo "Hello World" > foo.file cat foo.file Hello WorldPiping allows you to take the output from a program, and then run the output through another program.
cat /etc/passwd | grep username
Temporary Files
To create a uniquely named temp file, use $$ as either a prefix or suffix:> touch hello > ls hello > touch hello.$$ > ls hello hello.689
Return Values
The exit command takes one argument. A number to return. 0 is used to denote a successful exit, no errors occurred. Anything higher or lower than 0 normally means an error has occurred.
#!/bin/bash if [ -f "/etc/passwd" ] then echo "Password file exists." exit 0 else echo "No such file." exit 1 fi
getopts
Syntax:
getopts optstring name [args]getopts is used by shell procedures to parse positional parameters. optstring contains the option characters to be recognized; if a character is followed by a colon, the option is expected to have an argument, which should be separated from it by white space. The colon and question mark characters may not be used as option characters.
Here is an example:
while getopts ":d:j:u:p:r:t:a:" opt; do case $opt in d ) xdomain=$OPTARG ;; j ) xjobcode=$OPTARG ;; u ) xuser=$OPTARG ;; p ) xpasswd=$OPTARG ;; r ) xrealm=$OPTARG ;; t ) xtimeout=$OPTARG ;; a ) xargs="$xargs $OPTARG" ;; * ) echo $USAGE exit 1 ;; esac doneEach time it is invoked, getopts places the next option in the shell variable name, initializing name if it does not exist, and the index of the next argument to be processed into the variable OPTIND. OPTIND is initialized to 1 each time the shell or a shell script is invoked. When an option requires an argument, getopts places that argument into the variable OPTARG. The shell does not reset OPTIND automatically; it must be manually reset between multiple calls to getopts within the same shell invocation if a new set of parameters is to be used.
When the end of options is encountered, getopts exits with a return value greater than zero. OPTIND is set to the index of the first non-option argument, and name is set to ?.
getopts normally parses the positional parameters, but if more arguments are given in args, getopts parses those instead.
getopts can report errors in two ways. If the first character of optstring is a colon, silent error reporting is used. In normal operation diagnostic messages are printed when invalid options or missing option arguments are encountered. If the variable OPTERR is set to 0, no error messages will be displayed, even if the first character of optstring is not a colon.
If an invalid option is seen, getopts places ? into name and, if not silent, prints an error message and unsets OPTARG. If getopts is silent, the option character found is placed in OPTARG and no diagnostic message is printed.
If a required argument is not found, and getopts is not silent, a question mark (?) is placed in name, OPTARG is unset, and a diagnostic message is printed. If getopts is silent, then a colon (:) is placed in name and OPTARG is set to the option character found.
getopts returns true if an option, specified or unspecified, is found. It returns false if the end of options is encountered or an error occurs.