Bash Shell Tips and Tricks
by James D. Keeline
The Bourne Again Shell (/bin/bash) is an extension of the original Bourne Shell (/bin/sh) and it generally the most popular shell for the Linux operating system. In Unix systems, the C Shell (/bin/csh) and its successor (/bin/tcsh) are more popular. Other available shells are the Korn Shell and Z Shell.
The purpose of the shell is to provide interaction between the user and the operating system. It alows a user to type in commands and receive output from the system. Most of the commands on a Linux system are executable binary programs or scripts written in various languages. Many shells, including Bash, offer script programming to combine the above into custom programs.
To write an effective Bash program, one should begin with an appropriate header like this:
#!/bin/bash
# myscript
# By James D. Keeline 2003-04-07
The first line is called the "sha-bang" line, so named because the pound sign is used to denote a sharp note in music and the exclamation point is sometimes referred to as the bang character. The path and program specified indicates the location of the interpreter program which will process the lines in the script. This is similar to the sha-bang line used for Perl programs (#!/usr/bin/perl). This should be placed at the first character position in the first line of the program.
Comments in Bash scripts are noted with the pound sign. In this example the name of the script, author, and creation date are noted.
Like other Unix/Linux programs, Bash scripts have one input and two output channels. The input channel is known as Standard Input. The output channels are Standard Output and Standard Error. Programs can be linked together via either redirection or pipes.

In the case of redirection, Standard input may be read from a file or one or both outputs may be written to (or appended to) a file.
command < file # read file as input for command
command > file # execute command and send output to file
command >> file # execute command and append output to end of file
command 2> file # execute command and send Standard Error to file
command 2&> file # execute command and send both Standard Output and Error to file
command 2> /dev/null # execute command and throw error messages away
When a pipe (|) is used, the output of one program is sent to the input of another program or script.
command1 | command2 # send output of command1 to input of command2
An interesting variation of the above is the tee command which allows output to be sent to two places:
cmd1 | tee file | cmd2 # send output of command1 to file and input of command2
Other ways to input data into a program include command line arguments and options. Values which appear on the command line after the script name become positional parameters within the program when it is executed. The first nine are $1 $2 $3 $4 $5 $6 $7 $8 $9 the tenth value may be referred to as ${10} and so on.
program abc def xyz # $1="abc", $2="def", $3="xyz"
There are also several special variables assigned as well. Some useful ones include:
$0 # Full path and program name of script being executed
$# # Number of positional parameters in command line
"$@" # Positional parameters as a list "$1" "$2" "$3"
"$*" # Positional parameters as a string "$1 $2 $3"
$? # Value returned by last command
Variables are processed with Bash. When a variable is set a dollar sign is not used. However, when a variable value is accessed, the dollar sign is used. It is important that no spaces precede or follow the equal sign.
var2=$var1 # Place value of $var1 in $var2
There are also ways to access portions of values of variables.
Most Bash programs are executed in a top-down fashion. However, Bash offers many ways to control the structure of a program. These control statements include if-then-else, while-do-done, case and for-do-done. For example:
if [ sometest ]
then
# Statements if sometest is true
else
# Statements if sometest is false
fi
|
while [ sometest ]
do
# Statements to repeat until sometest is false
done
|
for countervar in $listvar
do
# Statements which use $countervar
done
|
until [ sometest ]
do
# Statements to repeat until sometest is true
done
|
case $testvar in
pattern1)
# Statements
;;
pattern2 | pattern3)
# Statements
;;
*)
# Default Statements
;;
esac
|
select name in $listvar
do
# Statements which use $name
done
|
Here are some brief sample programs which may be helpful:
#!/bin/bash
# mailme - send a text file as the body of an email message
EMAILADDRESS='user@server.com'
mail -s $1 $EMAILADDRESS < $1
|
#!/bin/bash
# codered - display IP addresses of CodeRed or Nimda attackers
grep -hE "default.ida?|cmd.exe" /var/log/httpd/access_log* |
cut -d' ' -f1 | sort | uniq -c
|
#!/bin/bash
# color -- this program takes a name of a color and some text
and then echoes out the text in the named color
b='[0;30m'
# Implement command-line options
while getopts "nr" opt
do
case $opt in
n ) o='-n' ;;
r ) b='' ;;
esac
done
shift $(($OPTIND - 1))
# Set variables
col=$1
shift
text="$*"
# Set a to console color code
case $col in
'black' ) a='[0;30m' ;;
'blue' ) a='[0;34m' ;;
'green' ) a='[0;32m' ;;
'cyan' ) a='[0;36m' ;;
'red' ) a='[0;31m' ;;
'purple' ) a='[0;35m' ;;
'brown' ) a='[0;33m' ;;
'ltgray' ) a='[0;37m' ;;
'white' ) a='[1;30m' ;;
'ltblue' ) a='[1;34m' ;;
'ltgreen') a='[1;32m' ;;
'ltcyan' ) a='[1;36m' ;;
'ltred' ) a='[1;31m' ;;
'pink' ) a='[1;35m' ;;
'yellow' ) a='[1;33m' ;;
'gray' ) a='[1;37m' ;;
esac
# Display text in designated color, no newline
echo -en "\033$a$text"
# If 'b' switch not on, restore color to black
if [ -n $b ]
then
echo -en "\033$b"
fi
# If 'n' switch on, do not display final newline
# otherwise output newline
echo $o
|
#!/bin/bash
# searchandreplace
# This program accepts three inputs: phrase to be changed, new phrase,
# and a list of files to act on.
# The program will read each file and use the sed command to do the
# search and replace. The new file will take the place of the old one.
# Store old text and new text in variables
old=$1;
new=$2;
# Shift positional parameters to places to the left (get rid of old and
# new from command line)
shift;
shift;
# Store list of files as a variable
files=$@;
a='';
for a in $files
do
temp=$(echo "/tmp/$LOGNAME-$a");
# echo "$temp";
echo -n ".";
sed -e "s/$old/$new/g" $a > $temp;
mv $temp $a;
done
echo;
echo -e "Searched $# files for '$old' and replaced with '$new'";
|