Frank Zappa

advertisement
Shell Scripting
• A script is a list of instructions that is
interpreted one instruction at a time
– we have to specify the interpreter as the first line
of the script as in #!/bin/bash or #!/bin/csh
• We write scripts as text files
– file must be executable, use 755 or 745
permissions
• With Bash, our scripts can include
– any Linux operation (e.g., date, chmod, useradd,
mount, stat, etc)
– with the Bash scripting language’s instructions
Sample Script
#!/bin/bash
date
du –s ~
find ~ -empty
• This script outputs to the terminal window
– The date
– A summary of disk usage for the user’s home directory
– Any empty files found under the user’s home directory
• Once written, we might save this in a file, say
report.sh and then execute it upon logging in
– we could type ./report.sh from the command line or
put the command report.sh in a file like .bashrc or
.profile
Controlling Script Output
• We might want to insert blank lines between
output
– add echo after date and du –s ~
• The find command could potentially give us a
lengthy list
– we might want to send the output to a textfile
• We could modify the script so that each
instruction is followed by >> outputfile
• Or we could invoke the script and redirect the
output
– ./report.sh > outputfile
Scripting Errors
• Scripts can generate run-time errors and
continue to run
– We would want to eliminate all errors before giving
the script to anyone to use
– Unfortunately there is no compiler to tell us syntax
errors and scripting error run-time messages can be
cryptic
#!/bin/bash
– Consider the script to the right
NAME=Frank Zappa
– No syntax error but running it yields echo $NAME
• ./somescript: line 3: Zappa command not found
– where somescript is the script name
– Why that error? What does it mean?
Scripting Errors
• The script below, when run by a non-root user,
gives us a different error
– wc: /etc/shadow: Permission denied
• In the previous script’s error, the error message
was caused by that script so the error reports the
script’s name
• Here, the error arises because wc does not have
permission to access the specified file
#!/bin/bash
wc –l /etc/passwd
wc –l /etc/group
wc –l /etc/shadow
Variables
• Variables can be used at any time in a script
– You do not have to declare them
• Variable names consist of letters, digits,
underscores
– Must start with a letter or underscore
• legal variables names: x, y, z, first_name, last_name, file1
• incorrect file names: 1_file, 2file, file 3 (no spaces allowed)
• Bash is case sensitive so first_name is not the same as
FIRST_NAME
• Variables store strings by default but can also
store integers
– Variables can also store arrays of strings/integers
Assignment Statements
• Used to store a value in a variable
– Format: VAR=VALUE
• VAR does not have to be previous declared
• VALUE can be
– literal value (placed in “” or ‘’ if there is at least one
blank space)
– the value stored in another variable, in which case we
retrieve the value using $VAR
– the result of some arithmetic or string operation
– the value returns by some function call or Linux
command – use $(command) or `command`
Assignment Statement: Examples
X=5
X stores the string 5
NAME=Frank
NAME stores Frank
NAME="Frank Zappa"
NAME stores Frank Zappa
NAME=Frank Zappa
An error arises because of the space
NAME=FrankZappa
No quote marks required here
NAME=ls *
An error should arise as Bash tries to execute *
X=1.2345
X stores 1.2345, but the value is a string, not a number
Assignment Statement: Examples
Assume FIRST_NAME stores Frank and LAST_NAME stores Zappa
NAME="$FIRST_NAME $LAST_NAME"
NAME stores Frank Zappa
NAME='$FIRST_NAME $LAST_NAME'
NAME stores $FIRST_NAME $LAST_NAME
NAME=$FIRST_NAME$LAST_NAME
Name stores FrankZappa
NAME=$FIRST_NAME $LAST_NAME
Results in an error because there is a space and no quote marks
NAME="FIRST_NAME LAST_NAME"
NAME stores FIRST_NAME LAST_NAME
NAME='FIRST_NAME LAST_NAME'
NAME stores FIRST_NAME LAST_NAME
Assignment Statement: With Linux Commands
• The following assignment statement assigns to
the variable DATE a combination of
– literal strings (Hello space, Today’s date and time
is space)
– the value stored in FIRST_NAME
– the result of calling the date command
– DATE="Hello $FIRST_NAME, today's date and
time is $(date) "
– DATE="Hello $FIRST_NAME, today's date and
time is `date`"
• the ` is known as a back tick or back quote mark,
located on the upper left key of the keyboard with the ~
Assignment Statement: With Linux Commands
• Consider the assignment statement LIST=$(ls)
• What is stored in LIST?
– the list of files/subdirectories of the current directory,
stored as a list
• LIST=$(ls –l)
– since this is stored as a list, the items are stored on after
another without line breaks
– performing echo $LIST to output the value stored in LIST
gives us output like the following (line breaks inserted here
for readability)
total 44 -rwxr--r--. 1 foxr foxr 448 Sep 6 10:20 addresses.txt -rwxr--r--. 1
foxr foxr 253 Aug 6 08:29 computers.txt -rw-rw-r--. 1 foxr foxr 235
Sep 11 14:23 courses.dat …
Declaring Variables
• As mentioned earlier, you do not have to declare variables
– But you can
• Format: declare [options] name[=value]
• Options are
–
–
–
–
–
–
–
+ to turn off the attribute and – to turn on the option
a – declare as an array
f – declare as a function
i – declare as an integer
r – declare as read-only
x – export the variable to the environment
declare –rx ARCH=386 declares ARCH as a read-only, exported
variable with the value 386 (since its read-only, ARCH can not
be changed except through another declare statement)
Assignment Statement: Arithmetic Operations
• By default, variables store strings even if the
strings they store are numbers
– By default, operations are taken to be string operations
(for instance, + is treated as a string not an operator) so
that if Y stores 5 then X=Y+1 results in X storing 5+1
instead of 6
– To override the default and have arithmetic operations
performed, start the assignment statement with the
word let or embed the operation on the right hand side
of the = in $((…))
– We can also use expr $((operation)) or where expr
returns a value which can be used in an echo statement
or the right hand side of an assignment statement
Assignment Statement: Arithmetic Operations
N=1
N=$((N+1))
let N+=1
let Y=$N+1
let Z=Y%N
Y=$(($Y+1))
let Y=$((Y<<2))
((Y++))
((Q--))
N=$N+1
N is now 2
Reassign N to be 1 greater, N is now 3
Y is 4, the $ in front of N is not needed so this could be let Y=N+1
Z is 1 (4 / 3 has a remainder of 1)
Y is now 5, we can omit the $ before Y
Y is now 20 (<< is a left shift or doubling, so this doubles Y twice)
Y is now 21
As Q had no value, it is now -1
N has the value of 3+1
X=$((Y+Z*))
yields the error message
Y+Z*: syntax error: operand expected (error token is “*”)
X=1 Y=2 Z=3
this statement assigns 3 variables their values
Assignment Statement: String Operations
• The expr statement can also be applied to one of
several string operations: substr, index, length
– expr command string [param(s)]
• the number of params depends on the command
– expr length string – returns the integer length of the
string
– expr substr string index length – returns the portion of
the string starting at index and length characters long
– expr index string string2 – return the index of the first
character in string that is also in string2
Assignment Statement: String Operations
• Assume PHRASE stores "hello world"
• X=`expr substr "$PHRASE" 4 6`
– X would store “lo wor”
• X=`expr index "$PHRASE" nopq`
– X would store 5
• X=`expr length "$PHRASE"`
– X would store 11
Parameters
• We can pass parameters to a script (similar to how we
pass parameters to a function)
• The parameters are supplied via the command line after
the script’s name as in
– ./somescript param1 param2 param3
• To utilize the parameters in the script use $1, $2, $3 etc
• $0 is the name of the script itself
• $# is the number of parameters supplied
– we might use this for error checking to make sure the script
received the right number of parameters
• $@ is the entire list of parameters
– we will use this in for loops
Parameters: Example
• This script receives 2 parameters which are
used to control three substr operations
#!/bin/bash
STR1="Hello World"
STR2="Frank Zappa Rocks!"
STR3="abcdefg"
expr substr "$STR1" $1 $2
expr substr "$STR2" $1 $2
expr substr $STR3 $1 $2
Assuming the script is called sub,
./sub 2 3 outputs
ell
ran
bcd
Notice the use of “” in the substr statements – required because STR1
and STR2 have blank spaces, but not needed in the third substr because
STR3 does not include any blank spaces
echo
• We explored this earlier when we looked at Bash
• In a script, echo serves much the same purpose
• Here are several examples
– echo Hello $NAME, how are you?
– echo Your current directory is $PWD, your home is
$HOME
– echo Your current directory is `pwd`, your home is ~
– echo The date and time are $(date)
– echo The number of seconds in a year is
$((365*24*60*60))
• echo has a couple of options
– -e – output escape characters non-literally (see next slide)
– -n – do not end with line break (next echo will start on
same line as this echo)
echo
Escape Character
\\
\a
\b
\n
\t
\v
\0###
\xHH
Meaning
Backslash
Bell (alert)
Backspace
New line
Horizontal tab
Vertical tab
ASCII character matching the given octal value ###
ASCII character matching the given hexadecimal value HH
echo –e "Hello World!\nHow are you today?"
Hello World
How are you today?
echo –e "January\tFebruary\tMarch\tApril"
January
February
March
April
echo –e "\x40\x65\x6C\x6C\x6F"
Hello
echo –e Hello\\World
Hello\World (recall that the quote marks are not needed for \\)
echo
• Here is an interesting example that
demonstrates how “” and ‘’ can differ
– LIST=*.txt
– assume the current directory contains three .txt
files, file1.txt, foo.txt, someotherfile.txt
echo $LIST
file1.txt foo.txt someotherfile.txt
echo "$LIST"
*.txt
echo '$LIST'
$LIST
read
• The input statement
• After read, place the variable name(s) to store the items
input
– you can have as many variables in one read statement as desired
• You should prompt the user before a read by outputting a
message indicating what the user should input
– this can either be through an echo (or echo –n) statement or by
using –p “string” with read
• see the next slide
• The option –N number limits input to number characters as
in –N 10 to limit the input to the first 10 characters entered
• The option –t seconds causes the script to only wait
seconds before timing out and continuing with the rest of
the script
– placing NULL values in any variables in the read statement
read
• echo Enter your name
• read NAME
Enter your name
Frank
• echo –n Enter your name
• read NAME
Enter your nameFrank
• echo –n “Enter your name
• read NAME
”
• read –p “Enter your name
” NAME
Enter your name
Enter your name
Frank
Frank
read
• What happens if the user’s input doesn’t match the number
of variables?
– read X
– read Y
– user inputs 5 10 <enter> 20 <enter>, then X stores the list 5 10,
Y stores 20
– read X Y
– user inputs 5 <enter>, X stores 5, Y is the NULL value
– read X Y
– user inputs 5 10 15 <enter>, X stores 5, Y stores the list 10 15
• While lists or the NULL value are acceptable for variables,
your script may cause an error if you try to operate on a list
as if it were assumed that each variable is storing one
number
– Z=$((X+Y)) causes an error if X or Y is NULL or stores a list
Selection Statement: Conditions
• We compare two strings using == and !=
• We compare two integers using –eq, -ne, -lt, -le, gt, -ge
• Conditions are placed in [ ] and each part of the
condition must be separated by spaces
– [ $x –gt 0 ]
– [ $FIRST == Frank ]
• notice that [$FIRST==Frank] yields a bizarre error message:
[Frank: command not found
– If we are comparing strings and the string has a blank
space, you must put the string in “” as in
– [ “$NAME” == “Frank Zappa” ]
• omitting the “” around $NAME yields an error if $NAME is
a string with at least one blank space
Selection Statement: Conditions
• A new syntax in Bash allows you to forego the
$, spaces and to use <, >, !=, <=, >=, == in
place of the 2-letter codes
– Place the entire condition in ((…))
[ $X –ne $Y ]
[ $Z –eq 0 ]
[ $AGE –ge 21 ]
[ $((X*Y)) –le $((Z+1)) ]
((X!=Y))
((Z==0))
((AGE>21))
((X*Y<Z+1))
Selection Statement: Conditions
• We can also use compound conditions by
combining conditions using boolean operators
AND (&&) and OR (||)
• The condition must be placed inside [[ ]]
notation or (( )) notation
[[ $X –eq 0 && $Y –ne 0 ]] – true if X is 0 and Y is not 0
[[ $X –eq $Y || $X –eq $Z ]] – true if X equals either Y or Z
[[ $X –gt $Y && $X –lt $Z ]] – true if X falls between Y and Z
(( X > Y || X < Y – 1 )) – true if X is greater than Y or less than Y-1
NOTE: These are wrong:
[[ $X –eq $Y –eq $Z ]]
[[ $X –eq $Y && $Z ]]
Selection Statement: File Conditions
• We can also test properties of files using the
notation
– [ -condition filename ] or [ ! –condition filename ]
Comparison Operator
-e
-f
-d
-b
-c
-p
-h, -L
-S
-r, -w, -x
-u, -g
-O, -G
-N
Meaning
File exists
File is regular (not a directory or device type file)
File is a directory
File is a block device
File is a character device
File is a named pipe
File is a symbolic link
File is a domain socket
File is readable, writable, executable
User ID/Group ID set (the ‘s’ under executable permissions)
You or your group owns the file
File has been modified since last read
Selection Statement: File Conditions
• [ -f file1.txt ] is true if file1.txt exists and is a regular file.
• [ -h file1.txt ] is true if file1.txt is a symbolic link to another file.
• [[ -r file1.txt && -w file1.txt ]] is true if file1.txt is both readable and
writable.
• [ ! –e $FILENAME ] is true if the file whose name is stored in the
variable FILENAME does not exist.
• [ -O file1.txt ] is true if file1.txt is owned by the current user.
• [ ! file1.txt –nt file2.txt ] is true if file1.txt is not newer than file2.txt.
• [ $FILE1 –ef $FILE2 ] is true if the file whose name is stored in the
variable FILE1 is the same as the file whose name is stored in the
variable FILE2
• We can also test variables to see if they are NULL [ -z $VAR ] or have a
value [ -n $VAR ]
Selection Statement: if-then
• Format: if [ condition ]; then action(s); fi
• The ; can be omitted if each part of this statement
is placed on a separate line
– if [ -e file1.sh ]; then ./file1.sh; fi
• if the file exists, execute it
– if [ $X –gt 0 ]; then COUNT=$((COUNT+1)); fi
• if the value in X is greater than 0 add 1 to COUNT
– if [ $NAME == $USER ]; then echo Hello master, I
am ready for you; fi
• specialized greeting if the value in NAME is equal to the
user’s login
– if [[ $X –ne 100 && $USER != Frank ]]; then X=0;
Y=100; NAME=Joe; fi
Selection Statement: if-then
if [ $NAME == $USER ]; then COUNT=$((COUNT+1)); fi
if [ $NAME == $USER ]
then
COUNT=$((COUNT+1))
fi
if [[ $X –ne 100 && $USER != Frank ]]; then X=0; Y=100; NAME=Joe; fi
if [[ $X –ne 100 && $USER != Frank ]]
then
X=0
Y=100
Note: indentation is strictly for
NAME=Joe
our readability and ignored by
fi
the Bash interpreter
Selection Statement: if-then-else
• In many situations, if the condition is false we
have an alternative action to perform
– We use an if-then-else statement
– format: if [ condition ]; then action(s); else
action(s); fi
• only one fi statement to end the entire instruction
– If the condition is true, do the then actions (known
as the then clause)
– If the condition is false, do the else actions (known
as the else clause)
Selection Statement: if-then-else
• if [ $SCORE –ge 60 ]; then GRADE=pass; else
GRADE=fail; fi
– the variable GRADE is assigned either pass or fail based on
the value in SCORE
• if [ $AGE –ge 21 ]; then echo You are allowed to
gamble, good luck; else echo You are barred from the
casino, come back in $((21-AGE)) years; fi
– this statement selects between two outputs based on
comparing your age to 21, notice the use of $((21-AGE)) in
the echo statement, this computes the number of years
under 21 that the user is
• if [ $X –le 0 ]; then echo Warning, X must be greater
than 0; else SUM=$((SUM+X)); fi
– we are adding X to SUM if the value in X is greater than 0
Select Statement: Nested Statements
• We can nest inside a then or else clause another
if-then or if-then-else statement
• This creates nested statements
– We usually do this when the logic is complex
– In the case of an “else if”, we use the word elif
• if [ $SCORE –ge 90 ]; then GRADE=A;
elif [ $SCORE –ge 80 ]; then GRADE=B;
elif [ $SCORE –ge 70 ]; then GRADE=C;
elif [ $SCORE –ge 60 ]; then GRADE=D;
else GRADE=F;
fi
– Notice only one fi statement for the entire construct
Select Statement: Nested Statements
In this example, we input 3 numbers and use nested if-then-else logic
To locate the largest of the three numbers, outputting the result
#!/bin/bash
read –p “Enter three numbers: ” X Y Z
if [ $X –gt $Y ]
then
if [ $X –gt $Z ]
then Largest=$X
else Largest=$Z
fi
elif [ $Y –gt $Z ]
then Largest=$Y
else Largest=$Z
fi
echo The largest number is $Largest
Select Statement: Nested Statements
#!/bin/bash
if [ $# -ne 3 ]
then echo Illegal input, did not receive 3 parameters
elif [ $2 == "+" ]; then echo $(($1+$3))
elif [ $2 == "-" ]; then echo $(($1-$3))
elif [ $2 == "*" ]; then echo $(($1*$3))
elif [ $2 == "/" ]; then echo $(($1/$3))
elif [ $2 == "%" ]; then echo $(($1%$3))
else echo Illegal arithmetic operator
fi
If the script is called calculator, we might call it with
./calculator 315 / 12 or
./calculator 41 + 815 but
./calculator 3 * 4 will lead to a problem because Bash will perform
filename expansion on the *! so we replace the 3rd elif with
elif [ $2 == "m" ]; then echo $(($1*$3))
Case Statement
• The nested if-then-elif-else statement is one
form of an n-way selection
• The case statement is the other
– Present each case as a list of possible values to
match against an expression
– Each case has one or more actions
– Format: case expression in
list1) action(s);;
list2) action(s);;
…
listn) action(s);;
esac
Case Statement
#!/bin/bash
if [ $# -ne 3 ]
then echo Illegal input, did not receive 3 parameters
else case $2 in
+) echo $(($1+$3));;
-) echo $(($1-$3));;
m) echo $(($1*$3));;
/) echo $(($1/$3));;
%) echo $(($1%$3));;
*) echo Illegal arithmetic operator;;
esac
fi
#!/bin/bash
Case Statement
echo What is your favorite color?
read color
Two additional examples
case $color in
(partial scripts)
red ) echo Like red wine ;;
blue ) echo Do you know there are no blue foods? ;;
green ) echo The color of life ;;
purple ) echo Mine too! ;;
* ) echo That’s nice, my favorite color is purple ;;
esac
echo –n Do you agree to the licensing terms of the program?
read answer
case $answer in
[yY] | [yY][eE][sS] ) ./program ;;
[nN] | [nN][oO] ) echo I cannot run the program ;;
* ) echo Invalid response ;;
esac
Case Statement
• We use * ) to indicate a default pattern so that if no
other pattern is selected, the last one is by default
• Also notice that a set of actions for a given pattern ends
with the notation ;;
– In some programming languages, after the action(s) is
performed, the next pattern is tested
– In this way, it is possible that multiple pattern/actions can
be selected
– For the Bash Case statement, we can indicate “continue
with further pattern checking” using ;& instead of ;; as
shown below
*[a-z]* ) … ;&
*[A-Z]* ) … ;&
*[0-9]* ) … ;;
*)…
Conditions Outside Selection Statements
• A shortcut approach to writing if statements is
available by combining a condition and an
action
• There are two forms
– [ condition ] && action
• perform the action if the condition is true
– [ condition ] || action
• Perform the action if the condition is false
• We could use this to replace
– if [ -f somescript.sh ]; then ./somescript.sh; fi
– [ -f somescript.sh ] && ./somescript.sh
Loops
• There are four forms of loops in Bash
– While loop – a conditional loop that tests a
condition and if true, executes the loop body
before testing the condition again
– Until loop – a conditional loop that tests a
condition and if false, executes the loop body
before testing the condition again
– For in loop – an iterator loop which iterates for
each item in a list
– For loop – newer syntax, similar to the C/C++/Java
for loop
Conditional Loops
Example:
Format:
while [ condition ]; do
action(s);
done
until [ condition ]; do
action(s);
done
Notice the two
loops use opposite
conditions
SUM=0
read –p "Enter the first number, negative to exit" VAL
while [ $VAL –ge 0 ]; do
SUM=$((SUM+VAL))
read –p "Enter next number, negative to exit" VAL
done
SUM=0
read –p "Enter the first number, negative to exit" VAL
until [ $VAL –lt 0 ]; do
SUM=$((SUM+VAL))
read –p "Enter next number, negative to exit" VAL
done
Conditional Loops
• The previous loops were sentinel loops
– Iterating based on a sentinel value, input by the user
– We can also have user-controlled yes/no loops
SUM=0
read –p "Do you have numbers to sum? " ANSWER
while [ $ANSWER == Yes ]; do
read –p "Enter the next number" VALUE
SUM=$((SUM+VALUE))
read –p "Do you have additional numbers?" ANSWER
done
– The following loop is controlled by computation
VALUE=1
while [ $VALUE –lt 1000 ]; do
echo $VALUE
$VALUE=$((VALUE*2))
done
Counter Controlled Loop
• Format:
– for (( initialization; condition; increment)); do action(s);
done
• As with other Bash instructions in (( )), we can use
arithmetic operations, omit $ for variables and use <,
>, etc
– We can also use shortcut statements for the increment as in
x++, n--, z+=3
• Examples
– for (( i=0; i<100; i++ )) – iterate from 0 to 99
– for(( i=0; i<100; i=i+2 )) – iterate from 0 to 99 by 2s
– for ((i=100;i>j;i--)) – iterate from 100 down to j, whatever
value j is storing
Iterator For Loop
• The traditional for loop in Bash iterates over a
list
– format: for VAR in LIST; do action(s); done
– LIST can be an enumerated list like (1 2 3 4 5), a
list stored in a variable, the value $@ (the input
parameters), or the result returned from a function
call or a Linux instruction like ls
– The loop iterates one time per LIST item
– For each loop iteration, the next value in LIST is
stored in VAR which we can use in the loop body
Iterator For Loop
• Examples
– Sum up all of the parameters of the script
#!/bin/bash
SUM=0
for VALUE in $@; do
SUM=$((SUM+VALUE))
done
echo $SUM
– Output the word count for each file passed as a
parameter (revised to the right) #!/bin/bash
#!/bin/bash
for filename in $@; do
wc $filename
done
for filename in $@; do
if [ -e $filename ]; then
wc $filename
fi
done
Shift Statement
• We can also iterate through the input
parameters by using a counter-controlled for
loop and the shift statement
– shift causes each parameter to move one position
down so that $2 moves into $1, $3 into $2, etc
#!/bin/bash
NUMBER=$#
SUM=0
for (( i=0; i<NUMBER; count++ )); do
SUM=$((SUM+$1))
shift
done
echo $SUM
The seq Instruction
• You can also generate a list of numbers to control
an iterator for loop using seq
– seq [first] [step] last
– returns the list of numbers from 1 to last (only given
last)
– returns the list of numbers from first to last (if given
first and last)
– returns the list of numbers from first to last skipping
over step each time (if given first, step and last)
#!/bin/bash
for number in `seq 1 2 10`; do
echo $number
done
Iterating Over Files
• We use the iterator for loop and either filename
expansion, or an ls command (or both)
#!/bin/bash
for filename in *.txt; do
if [ -w $filename ];
then echo $filename
fi
done
#!/bin/bash
TOTAL=0
for filename in *; do
if [ -f $filename ]; then
./$filename
TOTAL=$((TOTAL+1))
fi
done
echo $TOTAL scripts started
Iterating Over Files
• Here we are adding up the file sizes of all regular
files in the current directory
• We use awk to withdraw the size as provided by
the du command
#!/bin/bash
for file in *; do
if [ -f $file ]; then
TEMP=`du $file | awk '{print $1}'`
TOTAL=$((TOTAL+TEMP))
fi
done
echo Total file size for regular files in ~ is $TOTAL
Example
• Here we see a script that receives two parameters,
a filename and a string
• It counts the number of occurrences of the string
($2) in the file ($1)
#!/bin/bash
count=0
if [ $# -ne 2 ]; then echo ERROR, illegal number of parameters
elif [ ! –f $1 ]; then echo ERROR, $1 is not a regular file
else
for word in `cat $1`; do
if [ $word == $2 ];
then count=$((count+1))
fi
done
echo The number of occurrences of $2 in $1 is $count
fi
The while read Statement
• We can combine the while and the read
– This loops while there is still input available
– Format: while read varlist; do action(s); done
• The user will have to input every item in varlist
for every iteration, ending with control+d
– Or, we can redirect a text file to be used as input by
calling the script as ./script < inputfile
– Or, we can redirect the input from a textfile by
specifying done < inputfile
Examples
#!/bin/bash
Input from keyboard unless executed with
SUM=0
a redirection (/script < inputfile)
while read X Y; do
SUM=$((SUM+X*Y))
done
echo $SUM
#!/bin/bash
Input must come from data file data.txt
SUM=0
while read X Y; do
SUM=$((SUM+X*Y))
done < data.txt
echo $SUM
Arrays
• An array is a container structure – it stores
multiple values
• An array in most languages is a homogenous
structure in that each value must be of the same
type
• In Bash, the array stores string or integers or a
combination of the two
• We access into the array by using an array index
where the first index is given the number 0
– the notation is ${a[index]} as in ${a[0]} or ${a[5]}
– an array of n items is numbered 0 to n-1
Arrays: Declaration, Initialization
• You can declare an array using either
– declare –a arrayname
– arrayname=( )
• You can initialize an array using
–
–
–
–
–
arrayname=(value1 value2 value3 …)
arrayname[index1]=value1
arrayname[index2]=value2 …
or
arrayname=([0]=value1 [1]=value2 [2]=value3…)
• And you can combine the two
– declare –a ARR=(apple banana cherry date)
Arrays: Initialization
• You do not have to use consecutive or ordered
indices when initializing an array
– ARR=([0]=apple [2]=banana [4]=cherry [6]=date)
• although there is no good reason to do this either!
– In this example, ARR[1], ARR[3] and ARR[5] are
currently empty and can be used later
Arrays: Accessing Elements
• To put a value into an array element
– ARR[index]=…
• To pull a value out of an array element
– =${ARR[index]}
– echo ${ARR[index]}
• You can also obtain the full array as a list
–
–
–
–
${array[@]}
${array[*]}
“${array[@]}” – returns the list with each item quoted
“${array[*]}” – returns the entire list as one quoted
item
– ${#array[@]} and ${#array[*]} – return number of
elements in the array
Array: Iterating Over
• for value in ${a[@]}; do ...$value… ; done
– $value stores each array value
• for (( i=0; i<${#a[@]}; i++)); do …${a[i]}… ;
done
– ${a[i]} stores each array value
• We would use the former case usually because it
is easier (less code, easier to understand)
• However, if we wanted to skip over elements we
might use a variation of the second approach, for
instance initializing i to 1 and increment i=i+2
would access every odd element
Array: Example Script
#!/bin/bash
list=(www.nku.edu/~foxr/CIT371/file1.txt
www.uky.edu/cse/welcome.html
www.uc.edu/cs/csce310/hw1.txt
www.xu.edu/welcome.txt
www.cs.ul.edu/~wul/music/lyrics.txt)
for i in ${list[@]}; do
wget $i
if [ ! –e $i ]; then
echo Warning, $i not found!
fi
done
Use wget to retrieve a
number of URLs and
output a warning if a
particular URL is not
found
Alternate for loop:
for (( i=0; i<${#list[@]}; i++)); do
url=${list[i]}
wget $url
if [ ! –e $url ]; then
echo Warning, …
fi
done
String Manipulation
• Recall expr substr string index length
• A shortcut is ${string:index:length}
– Except that index in this latter approach starts
counting at 0 instead of 1
– So `expr substr $string 3 4` = ${string:2:4}
• Example to obtain initials from First, Middle,
Last
– Initials="`expr substr $First 1 1` `expr substr
$Middle 1 1` `expr substr $Last 1 1`"
– Initials=${First:0:1}${Middle:0:1}${Last:0:1}
String Regular Expression Matching
• Format: `expr match string ‘\(regex\)’
echo `expr match "abcdefg" '\([a-dg]*\)'`
abcd
our regex only matches, in order, a, b, c, d and g, so the match stops after d
echo `expr match "abcdefg" '\([a-dg]*$\)'`
matches nothing because the $ requires that it match at the end of the
string but the match begins at the front of the string and the g is separated
from abcd by two characters that are not part of the regex
echo `expr match "abcdef123" '\([a-z]*\)'`
abcdef
cannot match the digits
echo `expr match "abcdef123" '\([a-z]*[0-9]\)'`
abcdef1
only matches one digit
echo `expr match "abcdef123" '\([a-z][0-9]\)'`
will not match because there is no digit in the second position
Functions
•
•
•
•
•
A function is a subroutine
It supports reusable code and modularity
A function is a stand-alone piece of code
When called, we can pass it parameters
In Bash, we define a function either as
– name ( ) { … }
• No parameters are listed in the ( ), the ( ) must be empty
– or
– function name {…}
Functions: Defining a Function
foo( ) {
echo Function foo
echo Illustrates syntax
}
function foo {
echo Function foo
echo Illustrates syntax
}
foo( ) { echo Function foo; echo Illustrates syntax; }
Functions: Calling a Function
• The function must be defined in the script before it
can be called
– To call a function, just list it by name with no parens
• foo
– Or, supply it with parameters like we supply parameters
to a script, list them after the function name
• foo 1 2 3 4 5
– The function can be defined in another script as long as
that script is executed before we call the function
• For instance, if we have a function, foo, defined in the file bar,
we would need to have
– ./bar (or source bar)
• Before we have
– foo
Functions: Example
#!/bin/bash
// script instructions
// function f1 defined
// more script instructions
// call to f1
// more script instructions
The format of a script that has a function
defined and then used
#!/bin/bash
runIt( ) {
if [[ -f $1 && -x $1 ]]; then ./$1
else echo Error, $1 does not exist or not executable
fi
}
for file in $@; do
runIt $file
done
Functions: Local Variables
• We can declare variables to be
local to a function
– local var1 var2 var3 …
– If we do so, then these variables
are known only within the
function
– Otherwise, if variables are used
in the function, they are also
known in the script after the
function terminates
• the code to the right outputs 10 5 5
• notice how TEMP retains its value
after the function terminates
#!/bin/bash
X=5
Y=10
swap( ) {
TEMP=$X
X=$Y
Y=$TEMP
}
swap
echo $X $Y $TEMP
Functions: return Statement
• Functions in most programming languages
return a value
– We might for instance write a function to compute
and return a value like a function to compute the
square root of a parameter
– In Bash, we can explicitly use a return statement
– The return statement returns an integer value
• this value is stored in the special variable $?
• To view its value, use an echo $? after the function call
– foo 1 2 3 4 5
– echo $?
Functions: exit Statement
• The exit statement is very similar to the return
statement in that it returns an integer
– however, with exit, the integer should be 0 or
positive only
• We will use exit to return an error code
– again, we can see the result in $?
• If the result is not 0, assume the function
yielded an error, we can use this for error
checking and correction
Functions: example
#!/bin/bash
maximum( ) {
if [ $# -eq 0 ]; then exit 9999
else
max=$1
shift
for num in $@; do
if [ $num –gt $max ]; then max=$num; fi
done
fi
return $max
}
maximum 5 1 2 6 4 0 3
echo $?
Functions: Example
#!/bin/bash
X=0
#!/bin/bash
X=0
foo( ) {
foo( ) {
X=$1
X=$((X+1))
echo $X
}
local X
X=$1
X=$((X+1))
echo $X
Prints 6
Prints 6
}
echo $X
foo 5
echo $X
Prints 0
Prints 6
echo $X
foo 5
echo $X
Prints 0
Prints 0
Download