Difference between revisions of "CSC231 Bash Tutorial 6"
(→Script Files) |
(→Challenge #5:) |
||
Line 408: | Line 408: | ||
<br /> | <br /> | ||
+ | =Arithmetic With Bash Variables= | ||
+ | <br /> | ||
+ | * The Linux '''expr''' command can be used to do simple arithmetic. Try these commands at the prompt: | ||
+ | |||
+ | 231b@aurora ~/handout $ expr 3 + 4 | ||
+ | |||
+ | 231b@aurora ~/handout $ count=10 | ||
+ | 231b@aurora ~/handout $ echo $count | ||
+ | 231b@aurora ~/handout $ expr $count + 2 | ||
+ | |||
+ | 231b@aurora ~/handout $ expr $count - 7 | ||
+ | |||
+ | 231b@aurora ~/handout $ expr $count \* 2 | ||
+ | |||
+ | 231b@aurora ~/handout $ expr $count / 3 | ||
+ | |||
+ | 231b@aurora ~/handout $ expr $count % 4 | ||
+ | |||
+ | * Note: the reason we use "\*" when we want to multiply is because otherwise Bash would see a '*' character on the command line, which it automatically assumes to mean "All files in the current directory." | ||
+ | |||
+ | |||
+ | |||
<br /> | <br /> | ||
<showafterdate after="20170324 11:40" before="20170601 00:00"> | <showafterdate after="20170324 11:40" before="20170601 00:00"> |
Revision as of 15:10, 23 March 2017
--D. Thiebaut (talk) 22:18, 21 March 2017 (EDT)
This lab deals with bash script files. Script files are program files that contain bash commands, and these commands can be interpreted by bash, one after the other, as if the user had typed them at the keyboard. Scripts can be quite complex, and contain functions, if-statements, loops, variables and other programming concepts found in programming languages.
|
Reference
- This Bash tutorial heavily uses material presented in the excellent set of pages "Ryan's Tutorials" at http://ryanstutorials.net/bash-scripting-tutorial/. Do not hesitate to study these tutorials if you need additional information.
Backing up your files
Just in case you mess up and erase files in your account by mistake, you will make an archive of all your files and save it in your instructor's account:
cd tar -czvf backup2.tgz * rsubmit backup backup2.tgz
That's it! An archive of all your file should now be saved and available in case of accident!
Script Files
Let's create a simple batch file that will allow you to automatically assemble, link, and run an assembly program. Furthermore, we'll link it with the 231Lib library, just to be safe. If it doesn't use it, it should be fine.
The commands you normally use to go through this process are the following:
nasm -f elf progName.asm ld -melf_i386 progName.o 231Lib.o -o progName ./progName
Let's create a script file called nald (for nasm ld) that will run these 3 commands automatically. For now, just copy the code without worrying too much about what happens. We'll explain what's going on later.
- emacs a new file called nald
- Store the following lines in it:
#! /bin/bash nasm -f elf $1.asm nasm -f elf 231Lib.asm ld -melf_i386 $1.o 231Lib.o -o $1 ./$1
- Make sure that the #-sign is the first character on the first line of the script.
- Make the script executable:
chmod +x nald
- chmod is a command that changes the permissions of a file. "+x" means that everybody including you, everybody in the 231 class, and everybody who has an account on Aurora can execute your script.
- now run the script and pass it the name of one of your assembly files. Let's assume that you have a file called helloWorld.asm in your directory:
./nald helloWorld
- You should see the output of your program printed on the screen.
- Using emacs, make a slight modification to your helloWorld.asm program. Maybe, make it print something different.
- reassemble and run it in one command:
./nald helloWorld
- You now have a nice script that will save you several keystrokes when you create your next assembly programs!
Explanations
Shebang
- #! /bin/bash
- the first line of the script starts with the pair "#!" which is referred to as "shebang" in the Linux world. It indicates that the commands in the script should be executed by the bash shell, which is located in the /bin directory of Aurora. If we had Python program that we would want to run automatically, we would use a shebang with Python's path. Let's try that for fun:
- Create a new file with emacs called hello.py
#! /usr/bin/python from __future__ import print_function def main(): print( "Hello world of Python!" ) main()
- Save the file, and make it executable
chmod +x hello.py
- run the program from the command line:
./hello.py Hello world of Python!
- Notice that you didn't write "python hello.py" to run your program. Just "./hello.py." That's a nice trick.
- In summary, the shebang defines the interpreter to use to read the file and execute the lines.
$1
- Command line parameters
- inside the script, we can access the parameters that were typed on the command line using $1, $2, $3, and some others. To best understand how this works, create the following simple shell script with emacs called script1.sh:
#! /bin/bash echo "You typed $# words on the command line" echo "The whole collection of words is $@" echo "Word 0 is $0" echo "Word 1 is $1" echo "Word 2 is $2" echo "Word 3 is $3" echo "Word 4 is $4"
- Make the script executable:
chmod +x script1.sh
- And try it with several command line parameters:
./script1.sh hello world 3 20 2017 ./script1.sh CSC231
- so this is how the script can access the parameters passed on the command line. When you typed nald fileName on the command line earlier, the name of your file becomes $1 inside the script.
Challenge #1: |
In a recent lab you created a command with a for loop that would run the N-Queens program for several chessboard sizes.
A typical command to run the program for several board sizes would be:
for i in `seq 8 14` ; do echo -n "$i " java NQueens $i -q done
(if you have lost your NQueens program, you can get another copy with getcopy NQueens.java, and compile it with javac NQueens.java.)
- Create a bash script calls runNQueens.sh with the loop above in it. It will run all 7 cases automatically when called:
./runNQueens.sh 8 0.158674 ms 9 0.093859 ms 10 0.160577 ms 11 0.113491 ms 12 0.355657 ms 13 0.185683 ms 14 12.889715 ms
Challenge #2: |
Modify your script so that you can pass the boundaries of the board sizes you want to check, on the command line. For example, if you wanted to run the NQueens for board sizes ranging from 8 to 20, you'd run your script as follows:
./runNQueens.sh 8 20
Comments
- The #-sign is used as a comment. Bash scripts should be documented the same way regular programs are.
- Here's a documented version of the nald script. Please emulate this example when you write your bash scripts.
#! /bin/bash # nald # D. Thiebaut # General script to assemble, link, and run assembly # language programs. Note that the 231Lib library will # be linked automatically to the program, whether it needs # it or not. # # Syntax: nald programName (without the .asm extension) # -------- # assemble nasm -f elf $1.asm nasm -f elf 231Lib.asm # link ld -melf_i386 $1.o 231Lib.o -o $1 # execute ./$1
Scripts Behave as Bash Commands
- One nice feature of a bash script is that you can use it the same way you would a bash command. For example, if you wanted to capture the output of script1.sh in a file, you could simply type:
./script1.sh hello world 3 20 2017 > dummy.txt
- Check the contents of the file dummy.txt and verify that the output of the script was captured correctly.
Bash Variables
You have already used a bash variable when you wrote your first bash loop:
for i in `seq 1 10` ; do echo $i done
Rule for bash variables:
- When you create the variable, you do not use a $-sign.
- When you use the variable in an expression, you use the $-sign.
Setting Variables
- The syntax for setting a variable is simple:
variable=value
- Note that there are no spaces around the equal sign.
- Variables do not necessarily have to be used in scripts, you can also use them on the command line. Try these different commands on the Linux command line:
231b@aurora ~ $ semester=spring 231b@aurora ~ $ course=CSC231 231b@aurora ~ $ year=2017 231b@aurora ~ $ echo "$course, $semester $year"
- If you want to set variables to string that contain several words, then enclose the string in double quotes:
231b@aurora ~ $ name="Smith College" 231b@aurora ~ $ echo $name
- You can also set a variable to be the output of a command.
231b@aurora ~ $ asmFiles=`ls *.asm` 231b@aurora ~ $ objectFiles=`ls *.o` 231b@aurora ~ $ echo $asmFiles 231b@aurora ~ $ echo $objectFiles
- or the output of a pipe:
231b@aurora ~ $ noFiles=`ls /etc | wc -l` 231b@aurora ~ $ echo "There are $noFiles files in the /etc directory"
Concatenating Variable Names to Strings
- Sometimes it is useful to create several files that have the same name, except for a number that identifies them. For example data_1March.txt, data_2March.txt, data_3March.txt, data_4March.txt, etc.
for i in `seq 1 4`; do
fileName=data_$iMarch.txt
touch $fileName
echo "Created file $fileName"
done
- I you run this loop, you will discover that instead of creating 4 files, it only create one. This is because Bash does not figure out that $iMarch.txt is $i concatenated to March.txt. In order to help Bash figure out what is what, we can write the variable as ${i}, as illustrated in the new bash loop below:
for i in `seq 1 4`; do
fileName=data_${i}March.txt
touch $fileName
echo "Created file $fileName"
done
- List the files in your directory by oldest to newest and verify that you now have the 4 new wanted files: ls -ltr
Challenge #3: |
- Most Linux systems maintain a list of words in the file /usr/share/dict/words. Check this file out, and verify that it indeed contains a list of words.
- How many words are in the file?
- Bash also has a system variable called $RANDOM, which contains a different random number every time you use it. Verify that this is indeed the case on Aurora:
231b@aurora $ echo $RANDOM 231b@aurora $ echo $RANDOM 231b@aurora $ echo $RANDOM
- Your challenge: write a script called randomWord.sh that outputs a random word from the '/usr/share/dict/words file every time it is executed, as illustrated below.
231b@aurora $ ./randomWord.sh Iowans 231b@aurora $ ./randomWord.sh angora 231b@aurora $ ./randomWord.sh contractor 231b@aurora $ ./randomWord.sh Ford's
Challenge #4: |
- Modify the script you created for Challenge #3, and create a new one, called randomWord2.sh that accepts a string on the command line, and outputs a random word that contains that string.
- Here are examples of how it should work:
231b@aurora ~/handout $ ./randomWord2.sh A flack 231b@aurora ~/handout $ ./randomWord2.sh A Oregonian's 231b@aurora ~/handout $ ./randomWord2.sh A enumerated 231b@aurora ~/handout $ ./randomWord2.sh aba unabated 231b@aurora ~/handout $ ./randomWord2.sh aba unabated 231b@aurora ~/handout $ ./randomWord2.sh aba unabated 231b@aurora ~/handout $ ./randomWord2.sh al zoological 231b@aurora ~/handout $ ./randomWord2.sh al fallen 231b@aurora ~/handout $ ./randomWord2.sh al heterosexuals
- note that sometimes the word won't really be random, but we don't care for this challenge.
Challenge #5: |
- Linux always knows the current time and date. The command that returns it is date. Try this command.
- You can force date to output the information in a different format by providing a special string indicating what you want to see. For example:
231b@aurora $ date +%Y-%m-%d
- Create a new bash script called dateTag.sh to which you pass the name of a file on the command line, and that will make a copy of this file and prefix the name of the file with the current date. For example, if today's date is 2017-03-24, and you have a file in your directory called log.txt, then
231b@aurora $ dateTag.sh log.txt
- will create a copy of log.txt with the name 2017-03-24_log.txt in your directory.
- Here is an example.
231b@aurora $ touch dummy.txt 231b@aurora $ dateTag.sh dummy.txt 231b@aurora $ ls -ltr | tail -2 -rw------- 1 231b 231b 166 Mar 24 11:24 dummy.txt -rw------- 1 231b 231b 166 Mar 24 11:25 2017-03-24_dummy.txt
Arithmetic With Bash Variables
- The Linux expr command can be used to do simple arithmetic. Try these commands at the prompt:
231b@aurora ~/handout $ expr 3 + 4 231b@aurora ~/handout $ count=10 231b@aurora ~/handout $ echo $count 231b@aurora ~/handout $ expr $count + 2 231b@aurora ~/handout $ expr $count - 7 231b@aurora ~/handout $ expr $count \* 2 231b@aurora ~/handout $ expr $count / 3 231b@aurora ~/handout $ expr $count % 4
- Note: the reason we use "\*" when we want to multiply is because otherwise Bash would see a '*' character on the command line, which it automatically assumes to mean "All files in the current directory."
<showafterdate after="20170324 11:40" before="20170601 00:00">
Solution to Challenges
- Challenge 1
#! /bin/bash # runNQueens.sh # D. Thiebaut # runs and times the execution of the NQueens # program for board sizes ranging from 8 to 14. for i in `seq 8 14` ; do echo -n "$i " java NQueens $i -q done
- Challenge 2
#! /bin/bash # runNQueens.sh # D. Thiebaut # runs and times the execution of the NQueens # program for board sizes ranging from the first to second # parameters passed on the command line. # Example: # ./runNQueens.sh 10 21 # for i in `seq $1 $2` ; do echo -n "$i " java NQueens $i -q done
- Challenge 3
#! /bin/bash # randomWord.sh # D. Thiebaut # Outputs a random word every time it is executed. head -n $RANDOM /usr/share/dict/words | tail -n 1
- Challenge 4
#! /bin/bash # randomWord2.sh # D. Thiebaut # # outputs a random word containing the string passed on the command line. key=$1 words=/usr/share/dict/words grep -i $key $words | head -n $RANDOM | tail -n 1
- Challege 5
#! /bin/bash # dateTag.sh # D. Thiebaut tag=`date +%Y-%m-%d` fileName=$1 cp $fileName ${tag}_$fileName
</showafterdate>