We'll start with the makethumbnail program that we were working on last lecture.
In the intro to CS classes we learned that copy and pasting code is bad. In this case we have the two djpeg lines that are very similar. Can we do better? Yes, if we can use VARIABLES, which bash has.
xsize=100
ysize=150
if [ $# == 4 ]
then
xsize=$3
ysize=$4
elif [ $# != 2 ]
then
echo "$0: need 2 arguments: source-jpeg destination-file x-size y-size"
exit 1
fi
djpeg < $1 | pamscale -xysize $xsize $ysize | cjpeg > $2
We refer to variables by name with $.
How are these variables different than the variables we are used to in Java?
public static void foo(int i) {
if (i > 10) {
String str = "hello world";
System.out.println(str);
}
// "str" is not visible here
}
In bash however, variables are global - they are part of the "state" of the shell and there is no concept of scope - variables are visible to the entire script after they are defined.There are a number of shell variables that are used in bash itself and that have special meanings.
echo $PATH
What does the $PATH variable hold? The $PATH variable tells bash where to look for programs. This is how it knows where to find the "ls" program, for example. It holds a colon-separated list of directories where programs are stored. The shell will look in those directories whenever you issue a command. When you try to run makethumbnail directly, that is why it fails - because makethumbnail is not in any of the directories in your path:
$ makethumbnail
-bash: makethumbnail: command not found
But we can edit our PATH to add the directory that makethumbnail is in, and then we'll be able to run it just like any other command!
$ PATH=$PATH:/homes/mwinst/scripts
$ makethumbnail
$ PS1="hello world!"
hello world! ls
$ echo $HOME
/homes/mwinst
If you change HOME, you will change the location of your home directory.We've also seen the "export" command show up in the .bashrc file last week. But what does export mean?
If we have a shell script called "echox" that looks like the following:
#!/bin/bash
echo $x
And then we run it in the terminal like so:
$ x="hello world"
$ ./echox
Will it print "hello world"? The answer is no! The variable x that you set before running the script is NOT visible to the script. But you can make it visible using export:
$ export x="hello world"
$ ./echox
hello world
This is what we use in .bashrc - so that those variables that we set are visible to any other scripts that you run.
Let's write another script to output the fibonacci sequence. The fibonacci sequence is the sequence of numbers where the next one is the sum of the previous two.
1 1 2 3 5 8 13 21 ...
Let's just print the numbers that are less than 500. We'll need a loop to do this.
#!/bin/bash
first=1
second=1
echo $first
echo $second
while [ $second -lt 500 ]
do
sum=$((first+second))
echo $sum
first=$second
second=$sum
done
Here we also see how to do arithmetic expressions - we need to do $((expr)) - as well as numeric comparisons (-lt operator). In the condition statement of an if or while, you can also use standard logic operators like &&, ||, and !. Since all variables are actually strings, when you have the double parentheses the shell will try to use the strings as numbers. If a variable actually does NOT store a number, the shell will silently substitute 0.
What if we were to try to do addition as $first+$second? It wouldn't work! That's because all variables are actually stored as strings, and + will do concatenation instead. We need the double parentheses in order to signal to bash that we actually want an arithmetic operation.
Check out "file tests" as well - another option for the inside of an if/while statement. Modify our makethumbnail script to check whether the input image file actually exists, and print out an error message if it doesn't.
$ myvariable=6
$ myvariible=4 # creates new variable due to typo
$ myvariable=6
$ echo "$myvariible" # prints nothing
$ PATH=34 # overwrites the standard PATH variable
$ myvariable="two words"
$ myvariable=foo
$ echo $((foo+1))
1
Our final script of the day is called "countdown". This program will print out the arguments given to it in triangular design:
$ ./countdown a b c d e
a b c d e
b c d e
c d e
d e
e
I also introduced a command called "shift". "shift" takes the arguments given to script and discards the first one, "shifting" the arguments over by one position, as so:
# At the beginning, $1 is a, $2 is b, $3 is c, ...
shift
# Now, $1 is b, $2 is c, $3 is d, ...
How can we solve this problem with this command?
Approach one: use a for loop!
#!/bin/bash
# Save the number of arguments in a variable, because when we switch, the number changes
num=$#
for i in $(seq 1 $num)
do
echo "$@"
shift
done
There are a couple of new things going on here:
We could also do the same thing with a while loop:
while [ $# -gt 0 ]
do
echo "$@"
shift
done