In the print dialog, choose "Save as PDF" as the destination.
Operating Systems 1, Lab 12
Repeat work with while, until and for, then package it in functions that take arguments, return a status and keep their variables to themselves.
for (( )), files and "$@"break and continue, read a file with while read, build a menu with select$1, send back a status with return and a value with $( )local protects, and what a function can still change$?Part 1
Repeat while a test holds
While the test is true, keep running the body
#!/bin/bash
# Basic while loop
counter=1
while [ $counter -le 10 ]
do
echo $counter
((counter++))
done
echo All donecounter=1 sets the start value[ ], exactly as in ifdo and done((counter++)) adds 1, then back to the testdonePredict the output of while_loop.sh. Then change only its test to [ $counter -lt 10 ] and predict again.
$ bash while_loop.sh
1
2
3
4
5
6
7
8
9
10
All done$ bash while_lt.sh
1
2
3
4
5
6
7
8
9
All done#!/bin/bash
# Basic until loop
counter=1
until [ $counter -gt 10 ]
do
echo $counter
((counter++))
done
echo All donewhile, with the opposite meaning1 to 10, then All done, exactly like while_loop.shFrom the examples document. Predict what it prints.
#!/bin/bash
echo "Print the numbers which are even and divisible by 5"
counter=1
while [ $counter -le 20 ]
do
if [[ $counter%2 -eq 0 && $counter%5 -eq 0 ]]
then
echo "$counter"
fi
((counter++))
done
echo "Done"$ bash while.sh
Print the numbers which are even and divisible by 5
10
20
DoneIts comment says it prints odd numbers. Predict the first and the last number.
#!/bin/bash
n=20
until [ $n -lt 0 ]
do
if [[ $n%2 -gt 0 ]]
then
echo $n
fi
((n=$n-1))
done$ bash until.sh
19
17
15
13
11
9
7
5
3
1Part 2
Once for each item in a list
for var in list
do
commands
donevardo and doneFrom the examples document. Predict the lines for Tuesday and Sunday.
#!/bin/bash
for day in Monday Tuesday Wednesday Thursday Friday Saturday Sunday
do
if [[ $day == 'Monday' || $day == 'Thursday' ]]
then
echo "Meeting on $day at 9:30 am"
elif [[ $day == 'Tuesday' || $day == 'Wednesday' || $day == 'Friday' ]]
then
echo "Training on $day at 11:00 am"
else
echo "$day is Holiday"
fi
done$ bash for.sh
Meeting on Monday at 9:30 am
Training on Tuesday at 11:00 am
Training on Wednesday at 11:00 am
Meeting on Thursday at 9:30 am
Training on Friday at 11:00 am
Saturday is Holiday
Sunday is HolidayThe same list in a variable, used without and with quotes. Predict both outputs.
#!/bin/bash
planets='Mercury Venus Earth'
for planet in $planets
do
echo $planet
done
echo All done#!/bin/bash
planets='Mercury Venus Earth'
for planet in "$planets"
do
echo $planet
done
echo All done$ bash for_loop.sh
Mercury
Venus
Earth
All done$ bash for_quoted.sh
Mercury Venus Earth
All doneTwo dots make a range; two more set the step. Predict what each loop prints.
for value in {1..5}
for value in { 1..5 }
for value in {10..0..2}| Loop list | Really printed, one per line |
|---|---|
{1..5} | 1 2 3 4 5 |
{ 1..5 } (spaces inside) | { 1..5 }: three words, not a range |
{10..0..2} | 10 8 6 4 2 0: counts down by 2 |
Start, test and change in one line
#!/bin/bash
for ((num = 1; num <= 5; num++))
do
echo $num
done
echo All donenum = 1 runs once, before the loopnum <= 5 is the test, checked before every passnum++ runs at the end of every pass$ bash for_loop_alternate_syntax.sh
1
2
3
4
5
All doneA wildcard turns a folder into a list
#!/bin/bash
# Make a php copy of any html files
for value in $1/*.html
do
cp $value $1/$( basename -s .html $value ).php
done$ ls site
about.html index.html
$ bash copy_html.sh site
$ ls site
about.html about.php index.html index.php$1/*.html: every .html file in the folder given as $1basename -s .html site/about.html prints about$( ) puts that name into the cp command: site/about.php"$@" is the list of all the script's arguments
#!/bin/bash
echo "Argument values are: $1 $2"
for arg in "$@"
do
key=$(echo $arg | cut -f1 -d=)
value=$(echo $arg | cut -f2 -d=)
case $key in
id) echo "Student ID = $value";;
mark) echo "Obtained mark = $value";;
*)
esac
done$ bash cl2.sh id=2026101 mark=90
Argument values are: id=2026101 mark=90
Student ID = 2026101
Obtained mark = 90id=2026101cut -f1 -d=: the part before =; -f2: the part after itcase runs the line whose pattern matchesPart 3
break, continue, while read and select
Predict where the search stops.
#!/bin/bash
for value in {22..30}
do
if (( value % 7 == 0 ))
then
echo "found $value"
break
fi
echo "checked $value"
done
echo All done$ bash break_demo.sh
checked 22
checked 23
checked 24
checked 25
checked 26
checked 27
found 28
All donesecret.txt has had every permission removed (chmod 000). Predict the output.
#!/bin/bash
for value in $1/*
do
if [ ! -r $value ]
then
echo $value not readable 1>&2
continue
fi
cp $value backup/
echo "backed up $value"
done$ ls notes
a.txt b.txt secret.txt
$ bash copy_check.sh notes
backed up notes/a.txt
backed up notes/b.txt
notes/secret.txt not readable
$ ls backup
a.txt b.txtPredict: why does the first method print one line?
echo "Reading file using cat command"
content=`cat hardware.txt`
echo $content
echo "Reading file line by line using loop"
while read line
do
echo $line
done < hardware.txt$ bash readfile.sh
Reading file using cat command
Monitor Keyboard Mouse Scanner Printer
Reading file line by line using loop
Monitor
Keyboard
Mouse
Scanner
PrinterPS3 is the prompt. Predict what the invalid choice 5 prints.
#!/bin/bash
planets='Mars Venus Earth Quit'
PS3='Select planet: '
select planet in $planets
do
if [ "$planet" == 'Quit' ]
then
break
fi
echo Hello $planet
done
echo Bye$ bash select_example.sh
1) Mars
2) Venus
3) Earth
4) Quit
Select planet: 2
Hello Venus
Select planet: 1
Hello Mars
Select planet: 5
Hello
Select planet: 4
ByePart 4
Write it once, call it by name
#!/bin/bash
# Basic function
print_something () {
echo Hello I am a function
}
print_something
print_something$ bash function_example.sh
Hello I am a function
Hello I am a functionname () { ... } or function name { ... }() are decoration only: never put anything inside themPredict both lines of output.
#!/bin/bash
# Calling a function before it is defined
print_something
print_something () {
echo Hello I am a function
}
print_something$ bash call_before.sh
call_before.sh: line 3: print_something: command not found
Hello I am a functionWritten after the name; read inside as $1, $2, ...
#!/bin/bash
# Passing arguments to a function
print_something () {
echo Hello $1
}
print_something Mars
print_something Jupiter$ bash arguments_example.sh
Hello Mars
Hello Jupiterprint_something Mars: $1 is Mars$1Bash functions cannot return a value, but they can set a status. Predict the last line.
#!/bin/bash
# Setting a return status for a function
print_something () {
echo Hello $1
return 5
}
print_something Mars
print_something Jupiter
echo The previous function has a return value of $?$ bash return_status_example.sh
Hello Mars
Hello Jupiter
The previous function has a return value of 5The function prints the result; command substitution catches it
#!/bin/bash
# Setting a return value to a function
lines_in_file () {
cat $1 | wc -l
}
num_lines=$( lines_in_file $1 )
echo The file $1 has $num_lines lines in it.$1$( ) sends that text into num_lines instead of the screen$( ), it prints 3 on the screen and num_lines stays empty$ cat myfile.txt
Tomato
Lettuce
Capsicum
$ bash return_hack.sh myfile.txt
The file myfile.txt has 3 lines in it.Predict the three lines.
#!/bin/bash
# Experimenting with variable scope
var_change () {
local var1='local 1'
echo Inside function: var1 is $var1 : var2 is $var2
var1='changed again'
var2='2 changed again'
}
var1='global 1'
var2='global 2'
echo Before function call: var1 is $var1 : var2 is $var2
var_change
echo After function call: var1 is $var1 : var2 is $var2Before function call: var1 is global 1 : var2 is global 2
Inside function: var1 is local 1 : var2 is global 2
After function call: var1 is global 1 : var2 is 2 changed again| Line | var1 | var2 |
|---|---|---|
| Before the call | global: global 1 | global: global 2 |
| Inside the function | local: local 1 | global: global 2 |
| After the call | global, untouched: global 1 | global, changed inside: 2 changed again |
A message, a string left in a global, and an area calculated with bc
function print_message()
{
echo "Bash programming for beginner"
}
function ret_strdata()
{
return_str="Learn bash programming with functions"
}function calculate_area()
{
local radius=$1
area=$(echo $radius*$radius*3.14 | bc)
echo "Area of the circle is $area"
}print_message
ret_strdata
echo $return_str
echo "Enter the radius value"
read rad
calculate_area $rad$ bash func.sh
Bash programming for beginner
Learn bash programming with functions
Enter the radius value
4
Area of the circle is 50.24#!/bin/bash
# Create a wrapper around the command ls
ls () {
command ls -lh
}
ls$ bash override.sh
total 4.0K
-rwxr-xr-x 1 student student 85 Sep 25 09:00 override.shcommand ls skips the function and runs the real lscommand, ls calls itself again and again: nothing printsPart 5
Exit codes and four ways to call a script
Predict the answer for 55 and for 110.
#!/bin/bash
echo "Enter a numeric value"
read n
if [[ $n -le 100 ]]
then
exit 0
else
exit 1
fi#!/bin/bash
bash "first.sh"
if [ $? -eq 1 ]
then
echo "The input number is greater than 100"
else
echo "The input number is less than or equal to 100"
fi$ bash second.sh
Enter a numeric value
55
The input number is less than or equal to 100$ bash second.sh
Enter a numeric value
110
The input number is greater than 100From callpro.sh in the examples document, really run
| Call | What happens |
|---|---|
source add.sh | runs inside this script: its result (100) is still set afterwards |
bash subtract.sh 50 20 | a new Bash; its result (30) vanishes, ours is still 100 |
eval bash multiply.sh $a $b | builds the command line (6, 7), then runs it: 42 |
exec divide.sh 30 | replaces this script: prints 15, and no line after it runs |
Part 6
About 30 minutes: answer on paper, then run it in ~/lab12
About 4 minutes
#!/bin/bash
counter=10
until [ $counter -lt 4 ]
do
echo -n "$counter "
((counter-=3))
done
echo
for value in {15..3..4}
do
echo -n "$value "
done
echo$ bash p1.sh
10 7 4
15 11 7 3 About 5 minutes
even_odd.sh: print the numbers 1 to 10, one per lineis even or is odd1 is odd and 2 is even#!/bin/bash
for n in {1..10}
do
if [[ $n%2 -eq 0 ]]
then
echo "$n is even"
else
echo "$n is odd"
fi
done$ bash even_odd.sh
1 is odd
2 is even
3 is odd
4 is even
5 is odd
6 is even
7 is odd
8 is even
9 is odd
10 is evenAbout 6 minutes
grade: put $1 in a local mark; print A for at least 85, B for at least 65, else C92 70 40 85$( ) in g and print mark 92 gets Agrade () {
local mark=$1
if [ $mark -ge 85 ]
then
echo A
elif [ $mark -ge 65 ]
then
echo B
else
echo C
fi
}for m in 92 70 40 85
do
g=$( grade $m )
echo "mark $m gets $g"
done$ bash grade.sh
mark 92 gets A
mark 70 gets B
mark 40 gets C
mark 85 gets AAbout 4 minutes
#!/bin/bash
count=0
add () {
local step=$1
count=$(( count + step ))
step=100
}
step=7
add 5
add 3
echo "count=$count step=$step"$ bash scope.sh
count=8 step=7count has no local copy: both calls change the global, 0 + 5 = 5, then 5 + 3 = 8step is local in add, so step=100 changes only the local copy; the global stays 7About 6 minutes
valid: return status 0 for a mark from 0 to 100, status 1 otherwise45 120 100 -3 and call valid for each$? after each call: print 45 ok or 120 invalidvalid () {
if [[ $1 -ge 0 && $1 -le 100 ]]
then
return 0
else
return 1
fi
}for m in 45 120 100 -3
do
valid $m
if [ $? -eq 0 ]
then
echo "$m ok"
else
echo "$m invalid"
fi
done$ bash valid.sh
45 ok
120 invalid
100 ok
-3 invalidAbout 5 minutes
number_lines.sh: read hardware.txt with while read1: MonitorTotal: 5 lines#!/bin/bash
n=0
while read line
do
((n++))
echo "$n: $line"
done < hardware.txt
echo "Total: $n lines"$ bash number_lines.sh
1: Monitor
2: Keyboard
3: Mouse
4: Scanner
5: Printer
Total: 5 lineswhile runs while its test is true, until until it becomes true; check the first and the last passfor takes one item per pass: words, a quoted or unquoted variable, a range with a step, files, "$@"break leaves the loop, continue skips one pass, while read reads a file line by line$1, $2, ...return sets a status in $?; print a value and catch it with $( )local, or the function changes the globalsMahmoud Abas|Bash Scripting, Part 2: Loops and Functions