CT320: Network and System Administration

Fall 2018

Bash Scripts

See this page as a slide show

CT320: Bash Scripts

Bash Scripts

It’s a program

A shell script is a program. Therefore, it deserves all of the care that any other program should get, including, as appropriate:

Morons

Only an idiot would justify sloppy work with, “It’s only a shell script, so I didn’t bother doing …”.

Sure, if it’s a short-lived (you hope) program (whether script or not), then it may not require the full treatment. However, that decision is not determined by whether or not the program is a shell script, a Perl script, a Python script, or a C++ program.

Shell Scripting

Shell scripts are programs that:

Focus

There are many shells, including:

From now on, we will focus on bash.

Input and Output

Variables and Quoting

$ name=Bozo
$ let amount=2+7
$ echo "name amount"
name amount
$ echo "$name $amount"
Bozo 9
$ echo $UID
1038

Arguments

Script Syntax

Arguments example

$ echo -e '#! /bin/bash\nlet sum=$1+$2\necho "Sum is $sum"' >s
$ cat s
#! /bin/bash
let sum=$1+$2
echo "Sum is $sum"
$ ./s 34 5 12
.script: line 14: ./s: Permission denied
$ ls -l s
-rw------- 1 ct320 class 46 Sep 23 02:05 s
$ chmod +x s
$ ls -l s
-rwx------ 1 ct320 class 46 Sep 23 02:05 s
$ ./s 34 5 12
Sum is 39

if/then/else (numeric)

(( expression )) allows a C-like arithmetic expression as a condition:

cd /etc
let amount=$(ls | wc -l)
if (( amount == 0 ))
then
    echo "$PWD is empty."
elif ((amount==1))
then
    echo "$PWD contains one file."
else
    echo "$PWD contains $amount files."
fi
/etc contains 140 files.

case

Of course, as in most programming languages, there’s also a case construct:

cd /etc
let amount=$(ls | wc -l)
case $amount in
0) echo "$PWD is empty.";;
1) echo "$PWD contains one file.";;
*) echo "$PWD contains $amount files.";;
esac
/etc contains 140 files.

if/then/else (strings)

To compare strings, use [[]]. Unlike (()), spaces are required.

if [[ $HOSTNAME = boston || $HOSTNAME = denver ]]
then
    echo "You are in the CSB 120 lab."
elif [[ $HOSTNAME = *[aeiouy]* ]]
then
    echo "Well, at least $HOSTNAME contains a vowel."
else
    echo "$HOSTNAME confuses me. ☹"
fi
Well, at least parsons contains a vowel.

String comparison

if/then/else (files)

Filename expression example

Note that the operator is =. This does not mean to test for string equality, but, instead, to match the left side against the filename expression that is the right side.

if [[ $OSTYPE = linux* ]]
then
    echo "Great, we’re on something Linuxy: $OSTYPE"
else
    echo "Sorry, but the OS $OSTYPE is not supported."
fi
Great, we’re on something Linuxy: linux-gnu

Regular expression example

The =~ operator uses a regular expression, like the grep command. This is not a filename expression.

when=$(date)
if [[ $when =~ (Tue|Thu).*Aug ]]
then
    echo "Good."
else
    echo "Sorry, this only works on August Tuesdays/Thursdays."
    echo "Current date: $when"
fi
Sorry, this only works on August Tuesdays/Thursdays.
Current date: Sun Sep 23 02:05:57 MDT 2018

$?

$ date
Sun Sep 23 02:05:57 MDT 2018
$ echo $?
0
$ cp /etc/group xyz
$ echo $?
0
$ cp /etc/superman xyz
cp: cannot stat '/etc/superman': No such file or directory
$ echo $?
1
$ grep 'beet' /etc/hosts
10.1.44.127	beethoven.cs.colostate.edu beethoven
10.1.44.176	beet.cs.colostate.edu beet
$ echo $?
0
$ grep "cowabunga" /etc/hosts
$ echo $?
1
$ echo $?
0

if/then/else (program)

if grep -q "colostate.edu" /etc/resolv.conf
then
    echo "operating at CSU"
else
    echo "must be at home or out & about"
fi
operating at CSU

if/then/else (program)

Here’s the same code, written by someone who doesn’t understand how if/then/else works:

grep -q "colostate.edu" /etc/resolv.conf
if (($? == 0))  # or, worse: [[ $? -eq 0 ]]
then
    echo "operating at CSU"
else
    echo "must be at home or out & about"
fi
operating at CSU

if/then/else (general)

You can combine all three syntaxes using && and ||:

    if ((a>4)) && [[ -r infile ]] && cmp -s infile outfile.$a
    then
        echo "Holy Toledo, that was a miracle!"
    fi

Loops use the same syntax

while loops use the same argument as if. That is, you can have:

while loop example

let n=1
while ((n > 0))
do
    echo "n is $n"
    let n=n*32
done
n is 1
n is 32
n is 1024
n is 32768
n is 1048576
n is 33554432
n is 1073741824
n is 34359738368
n is 1099511627776
n is 35184372088832
n is 1125899906842624
n is 36028797018963968
n is 1152921504606846976

for loops (string-based)

for beatle in John Paul George Ringo
do
    echo "$beatle was one of the Beatles."
done
John was one of the Beatles.
Paul was one of the Beatles.
George was one of the Beatles.
Ringo was one of the Beatles.

for loops (string-based)

for datafile in /bin/*a*e*i*o*
do
    echo Processing $datafile
done
Processing /bin/build-jar-repository
Processing /bin/dbus-update-activation-environment
Processing /bin/gtk-update-icon-cache
Processing /bin/makeinfo
Processing /bin/ocamlbyteinfo
Processing /bin/pam-panel-icon
Processing /bin/pkla-check-authorization
Processing /bin/rebuild-jar-repository
Processing /bin/start-pulseaudio-x11
Processing /bin/system-config-authentication
Processing /bin/update-gtk-immodules

for loops (integer based)

A C-like arithmetic loop exists:

let max=5
for ((i=max; i>=0; i--))
do
    echo $i
done
echo "Blast off!"
5
4
3
2
1
0
Blast off!

Why not $max?

Arithmetic Loops

Or:

for i in {5..1}
do
    echo $i
done
echo "Blast off!"
5
4
3
2
1
Blast off!

Arithmetic evaluation

Primary attempt

Let’s try some straightforward code:

$ alpha=2+5
$ beta=alpha*3
$ echo beta
beta

Secondary attempt

Of course, echo beta simply yields “beta”. Perhaps a $ will help to extract the value of the variable:

$ alpha=2+5
$ beta=alpha*3
$ echo $beta
alpha*3

Tertiary attempt

We got the string “alpha” instead of its value. Let’s try $alpha:

$ alpha=2+5
$ beta=$alpha*3
$ echo $beta
2+5*3

A sure sign of guesswork: “let’s try”. Don’t guess. Know.

Quaternary attempt

Use let for arithmetic assignment:

$ alpha=2+5
$ let beta=$alpha*3
$ echo $beta
17

Quinary attempt

The problem was that $alpha literally returned the string 2+5. What is 2+5*3? Let’s avoid $:

$ alpha=2+5
$ let beta=alpha*3
$ echo $beta
21

Senary attempt

To be consistent, and to force arithmetic evaluation every step of the way, use let in the first assignment:

$ let alpha=2+5
$ let beta=alpha*3
$ echo $beta
21

Rules

Let’s boil this down into rules:

Comedy of errors

$ alpha=1
$ beta=2             # no spaces around = 
$ gamma=alpha+beta
$ echo gamma
gamma
$ echo $gamma
alpha+beta
$ delta=$alpha+$beta
$ echo $delta
1+2
$ let epsilon=alpha+beta
$ echo $epsilon
3

Arrays

Somewhat limited, and certainly inelegant, capabilities compared to C:

$ array=(one two three)
$ echo ${array[1]}
two
$ echo ${array[*]}
one two three
$ echo ${#array[*]}
3

Filename Expressions

Filename Expressions Examples

$ cd ~/bin
$ ls
checkin		      demo-script	  imv	old	 runner  vman
checkin-file-checker  domoss		  l	p	 save	 wikicat
checkin_prog	      e			  ll	peek	 scores  wikidiff
chit		      grade		  lsf	playpen  stats	 wikiedit
cls		      grade-busy	  moss	pwget	 tools	 wikigrep
cronedit	      grade-file-checker  new	ruler	 u	 wikiupdate
curve		      grades		  note	run	 unold	 wikiwhence
$ ls ad
/bin/ls: cannot access ad: No such file or directory
$ ls *ad*
grade  grade-busy  grade-file-checker  grades
$ ls [check]in*
/bin/ls: cannot access [check]in*: No such file or directory
$ ls checkin*
checkin  checkin-file-checker  checkin_prog

Regular Expressions

Levels of Regular Expressions

Beware of “levels” of regular expressions.

Obsolete stuff

made at imgflip.com

Use obsolete features only when dealing with obsolete computers.

if [ $PWD = / ]       # ☠ avoid single [ ]
if [[ $PWD = / ]]     # good

if [[ $n -eq 100 ]]   # ☠ avoid [[ … ]] for numbers
if (($n==100))        # better, but still poor
if ((n==100))         # good

now=`date`            # ☠ `backquotes` resemble 'quotes'
now=$(date)           # good

sum=`expr $sum + $n`  # ☠ invoking an external program‽
sum=$(expr $sum + $n) # ☠ not quite as bad
let sum=$sum+$n       # poor
let sum=sum+n         # ok
let sum+=n            # good

Script Example

#! /bin/bash
#
# Go into a temporary “playpen” directory; clean it up when done.

# If we can’t execute a file in TMPDIR, then change it to somewhere executable.
script=$(mktemp -t playpen-script-XXXXXX)
chmod u=rx,go= "$script"
"$script" 2>&- || TMPDIR=~/tmp
rm -f "$script"

cd "$(mktemp -d -t playpen-XXXXXX)"	# Create temporary dir.
cp -r ~/.playpen/* . 2>&-		# Files to play with
chmod -R u+rw .				# Works even if no files got copied.
ls -lhog | grep -v '^total '		# Show what’s here.
$SHELL
chmod -R u=rwx .			# Make everything removable.
cd /tmp					# Get away from temporary directory.
rm -rf $OLDPWD				# Remove previous, temporary, directory.

User: Guest

Check: HTML CSS
Edit History Source

Modified: 2018-08-30T14:53

Apply to CSU | Contact CSU | Disclaimer | Equal Opportunity
Colorado State University, Fort Collins, CO 80523 USA
© 2018 Colorado State University
CS Building