Imagine you find yourself typing the same set of commands daily (or maybe even multiple times per day). After doing this over and over, day after day, you will likely begin to ask yourself, “Is there another way?”
Some folks turn to the history list to make this easier. Recall from Chapter 2, “Working on the Command Line,” that you can re-execute previous commands either by pressing the up-arrow key until you see the correct command or by typing !cmd, replacing cmd with the command you want to execute. Sometimes this solution works, but history lists can change, and pressing the up-arrow key 75 times to get to a specific command presents a level of frustration in itself.
This is where scripting makes more sense. Scripting is essentially creating small programs based on the Linux commands that you want to routinely (or occasionally) execute. Scripting also provides some programming features, such as flow control and the passing of parameters to the script. In this chapter, you will learn how to create and read BASH shell scripts.
After reading this chapter and completing the exercises, you will be able to do the following:
Describe key features of different scripting languages, including Perl, Python, and BASH scripting.
Create and read BASH scripts.
Most Linux programming languages can be placed into two general categories: scripting languages (sometimes called “interpreted” languages) and compiled languages. There is no strict definition that separates these categories, but the following are the essential differences between the two:
• Complied languages cannot be executed directly from source code. The source code must be converted into compiled code first.
• Scripts traditionally are not compiled.
• Scripting languages are typically easier to learn.
• Scripts often take less coding to perform a task.
As an example of how these categories are not strictly defined, consider this: Perl is a popular scripting language that is executed directly from source code, but before executing it is compiled into memory, and the compiled form of the code is executed.
Because the scope of this book is focused on Linux, this chapter will concentrate on BASH scripting. However, you should be aware that other scripting languages are often used on Linux distributions. The following sections provide a brief overview of BASH, Perl, and Python scripting—the three most commonly used scripting languages on Linux.
In previous chapters, you learned the basics of working in Linux and the BASH shell. The commands you learned can also be used in shell scripting programs. For example, suppose you routinely execute the following commands:
cd /home ls -l /home > /root/homedirs du -s /home/* >> /root/homedirs date >> /root/homedirs
Instead of executing each of these commands manually, day after day, you can place all of the commands into a file, make the file executable, and then run the file as a program:
[root@onecoursesource ~]$ more /root/checkhome.sh #!/bin/bash cd /home ls -l /home > /root/homedirs du -s /home/* >> /root/homedirs date >> /root/homedirs [root@onecoursesource~]$ chmod a+x /root/checkhome.sh [root@onecoursesource~]$ /root/checkhome.sh
Because you can use Linux commands natively in BASH shell scripts, this scripting language can be extremely powerful. Another advantage of using this language is that you can be confident that just about every Linux (and Unix) distribution will have the BASH shell, making it easy to port a script from one system to another.
In addition to being able to use Linux commands in BASH shell scripts, you should be aware that this language has other programming features, such as the following:
• Variables
• Loop controls (if, while, and so on)
• Exit status values
• The ability to source code from other files
With all of its advantages, there are some disadvantages to BASH shell scripting:
• It lacks some advanced programming features, such as object-oriented programming.
• It is often much slower than executing other languages because each command is normally executed as a separate process.
Even with these disadvantages, BASH shell scripting is very popular in Linux. In fact, a search for BASH scripts (files that end in .sh) on a typical Linux distribution normally will result in hundreds of results:
[root@onecoursesource ~]$ find / -name "*.sh" | wc -l 578
Hint: The previous command provides you with the means to find existing BASH shell scripts. This is useful because learning BASH shell scripting includes reading existing scripts.
In the mid-1980s, a developer named Larry Wall began work on a new scripting language that would eventually be named Perl. At the time he was working on Unix-based systems that had tools such as the C programming language, the Bourne Shell scripting language (precursor to BASH), and sed and awk (more about these tools later). However, none of these tools worked as he wanted, so he created his own language.
Of course, Larry did not want to lose the features he did like about these tools, so he combined the features that he liked into his new language. This resulted in a language that looks a bit like C, a bit like shell scripting, and a bit like a hodgepodge collection of Unix utilities.
There are several aspects of Perl that Linux users like, including the following:
• You can very quickly write Perl code because much of what you need for basic scripting is already built in to the core language.
• Perl code is very flexible; you are not limited by the structure as much as some other languages.
• Perl’s syntax is fairly simple, derived primarily from the C language.
• It normally does not take very long to learn Perl.
• Perl has very powerful features, such as robust regular expressions.
Although Perl can be used for many different applications, it is often used for the following:
• Data parsing: Perl has powerful regular expression features, which makes it ideal for data munging (pulling chunks from data and generating reports).
• Web development: Perl is often a component of LAMP-based (LAMP = Linux, Apache HTTP Server, MySQL, and Perl or PHP) technology because of its web development features, including Common Gateway Interface (CGI).
• Code testing: Because Perl is easy and quick to code, developers will often use it to create tools to test their applications.
• GUI programs: Additional Perl modules (libraries), such as WxPerl and Tk, provide Perl programmers with the option of easily creating a GUI interface for users to interact with the Perl code.
• Administrative tools: System administrators will often create Perl scripts to help them automate administrative tasks.
Note
Perl scripting is a very large topic and beyond the scope of this book. However, as your experience in Linux grows, it is a language that you should consider learning because it has powerful features that allow you to automate tasks on Linux.
The beginnings of Python are best described by Guido van Rossum, its creator, who wrote the following as a forward for a book on Python published in 1996:
“Over six years ago, in December 1989, I was looking for a ‘hobby’ programming project that would keep me occupied during the week around Christmas. My office … would be closed, but I had a home computer, and not much else on my hands. I decided to write an interpreter for the new scripting language I had been thinking about lately: a descendant of ABC that would appeal to Unix/C hackers. I chose Python as a working title for the project, being in a slightly irreverent mood (and a big fan of Monty Python's Flying Circus).”
Little did he know that Python would one day become one of the world’s most popular scripting languages. Since that fateful Christmas break in the late 1980s, Python has developed into a robust programming language that is the core of many Linux tools and open source projects.
One of the driving philosophies of Python is well-structured code. Python enforces this with rules such as a very rigid indentation scheme. You can see how seriously Python developers take the concept of well-structured code by reading some of the rules defined by the document “Zen of Python”:
• Beautiful is better than ugly.
• Explicit is better than implicit.
• Simple is better than complex.
• Complex is better than complicated.
• Flat is better than nested.
• Sparse is better than dense.
• Readability counts.
In addition to Python being a well-structured language, the following components make Python a popular language:
• It has object-oriented features.
• It has a large standard library.
• It is extendable or embedded.
• The data structures provided by Python are more diverse than those of many languages.
Although Python can be used for many different applications, it is often used for the following:
• Network-based applications: By using Twisted, a Python-based network framework, you can develop network-based applications.
• Web development: The Apache Web Server provides the option of using Python scripts for dynamic websites.
• Scientific applications: Several libraries are available for Python that make it a good choice to create scientific applications.
• System tools: Python is often used by Linux developers to create system tools for the operating system.
Note
Python scripting is a very large topic and beyond the scope of this book. However, as your experience in Linux grows, it is a language that you should consider learning because it has powerful features that allow you to automate tasks on Linux.
Conversational Learning™ — Which Language Is Best?
Gary: Hi, Julia! I’m about to create a script, but I’m not sure which language is the best. I’m thinking of either Perl, Python, or BASH. Which one is the best language?
Julia: I believe trying to list the pros and cons of each scripting language is a mistake. To begin with, this is often a matter of opinion.
Gary: What do you mean?
Julia: For example, Perl is a very flexible language, whereas Python is more structured. If I want to write a quick script and I am not worried about maintaining the code long term, then flexibility might be a pro and structure a con. However, if I was working with multiple developers on a larger product, then structure might be a pro and flexibility a con.
Gary: Well, then, how do I decide which language to use?
Julia: Rather than trying to compare and contrast the pros and cons of each scripting language, try to focus on what people usually like about each language and what each language is typically used for. It is better to decide what aspect of a language is a feature versus a liability.
Gary: OK, I suppose this makes sense because if there was one “best language,” then everyone would just use that one language.
Julia: There you go—you got the idea.
To an extent, you already know many of the basics of BASH scripting because you have already learned many features of the BASH shell in this book. For example, you learned about shell variables in Chapter 2. Shell variables are used in BASH scripting to store values.
To start a BASH script, enter the following as the first line of the script file in a text editor, such as the vi or vim editor:
#!/bin/bash
This special sequence is called the shebang, and it tells the system to execute this code as a BASH script.
Comments in a BASH script start with a # character and extend to the end of the line. For example:
echo "Tux for President" #prints "Tux for President" to the screen
As shown in the previous example, the echo command can be used to display information to the user who is running a program. The arguments to the echo command can contain any text data and can also include the value of a variable:
echo "The answer is $result"
After creating your BASH script and saving it, you make it executable:
[student@onecoursesource ~]$ more hello.sh #!/bin/bash #hello.sh echo "Tux for President" [student@onecoursesource ~]$ chmod a+x hello.sh
Security Highlight
Scripts should never have the SUID permission set. This permission could allow someone to hijack the script and run commands as the owner of the script.
Now your code can be run as a program by using the following syntax:
[student@onecoursesource ~]$./hello.sh Tux for President
Note the need to place ./ before the name of the command. This is because the command may not be in one of the directories specified by the $PATH variable:
[student@onecoursesource ~]$ echo $PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games
To avoid the need to include ./ whenever you want to run your script, you can modify the $PATH variable to include the directory in which your script is stored. For example, typically regular users create a “bin” directory in their home directory and place scripts in this location:
[student@onecoursesource ~]$ mkdir bin [student@onecoursesource ~]$ cp hello.sh bin [student@onecoursesource ~]$ PATH="$PATH:/home/student/bin" [student@onecoursesource ~]$ hello.sh hello world!
In addition to the built-in variables that were discussed in Chapter 2, there are variables available in BASH scripts that represent the arguments passed into the script. For example, consider the following execution of a script called test.sh:
[student@onecoursesource ~]$ test.sh Bob Sue Ted
The values Bob, Sue, and Ted are assigned to variables within the script. The first argument (Bob) is assigned to the $1 variable, the second argument is assigned to the $2 variable, and so on. Additionally, all arguments collectively are assigned to the $@variable.
For additional details regarding these positional parameter variables or anything related the BASH scripting, consult the man page for BASH:
[student@onecoursesource ~]$ man bash
Several conditional statements are available for the BASH shell, including the if statement:
if [ cond ]
then
statements
elif [ cond ]
then
statement
else
statements
fi
Note the following:
• An “else, if” statement is spelled elif and is not required if you don’t want to perform additional conditional checks.
• After the if and elif, you need a then statement. However, after an else, do not include a then statement.
• End the if statement with the word “if” spelled backwards: fi.
See Example 15-1 for an example of an if statement.
Example 15-1 Sample if Statement
#!/bin/bash #if.sh color=$1 if [ "$color" = "blue" ] then echo "it is blue" elif [ "$color" = "red" ] then echo "it is red" else echo "no idea what this color is" fi
Note
BASH allows you to use either == or = for numeric equality.
In Example 8.1, the following conditional statement was used:
[ "$color" = "blue" ]
This syntax performs an implicit call of a BASH command named test that can be used to perform several comparison tests. This can include integer (numeric) comparisons, string comparisons, and file testing operations. For example, use the following syntax to test whether the string value that is stored in the $name1 variable does not equal the string stored in the $name2 variable:
[ "$name1" != "$name2" ]
What Could Go Wrong?
Common mistakes when creating an if statement include forgetting to put a then after the if and elif statements. Also, people often mistakenly put a then after an else statement, but this is incorrect.
Important Note
The spacing around the square brackets is very important. There should be a space before and after each square bracket. Without these spaces, an error message will occur.
Get in the habit of putting double quotes around your variables in BASH scripts. This is important in the event the variable has not been assigned a value. For example, suppose the script in Example 15-1 was executed with no arguments. The result would be that the color variable is unassigned and the resulting conditional statement would be if [ "" = "blue" ].
The result would be false, but without the quotes around $color, the result would be an error message and the script would exit immediately. This is because the resulting conditional statement would be missing one of its key components after the value of $color has been returned: if [ = "blue" ].
In addition to determining whether two strings are equal or not equal, you may also find the -n option useful. This option will determine whether a string is not empty, which is useful when testing user input. For example, the code in Example 15-2 will read data from user input (the keyboard), assign the input to the $name variable, and test to make sure the user typed something for the name.
Example 15-2 Testing User Input
[student@onecoursesource ~]$ more name.sh #!/bin/bash #name.sh echo "Enter your name" read name if [ -n "$name" ] then echo "Thank you!" else echo "hey, you didn't give a name!" fi [student@onecoursesource ~]$./name.sh Enter your name Bo Thank you! [student@onecoursesource ~]$./name.sh Enter your name hey, you didn't give a name!
If you want to perform integer (numeric) comparison operations, use the following:
• -eq: True if values are equal to each other.
• -ne: True if values are not equal to each other.
• -gt: True if first value is greater than second value.
• -lt: True if first value is less than second value.
• -ge: True if first value is greater than or equal to second value.
• -le: True if first value is less than or equal to second value.
You can also perform test operations on files and directories to determine information about the file’s status. These operations include the following:
• -d: True if the “file” is a directory.
• -f: True if the “file” is a regular file.
• -r: True if the file exists and it is readable by the user running the script.
• -w: True if the file exists and it is writable by the user running the script.
• -x: True if the file exists and it is executable by the user running the script.
• -L: True if the first value is less than or equal to second value.
In addition to if statements, the BASH scripting language has several other flow control statements:
• The while loop: Executes a block of code repeatedly as long as the conditional statement is true.
• The until loop: Executes a block of code repeatedly as long as the conditional statement is false. Essentially the opposite of a while loop.
• The case statement: Similar to an if statement but provides an easier branching method for multiple situations. The case statement ends with esac (“case” spelled backwards).
• The for loop: Executes a block of code for each item in a list of values.
The following code segment will prompt the user for a five-digit number. If the user complies, the program will continue as the condition of the while loop will be false. However, if the user provides incorrect data, the condition of the while loop will be true and the user will be prompted for the correct data again:
echo "Enter a five-digit ZIP code: "
read ZIP
while echo $ZIP | egrep -v "^[0-9]{5}$" > /dev/null 2>&1
do
echo "You must enter a valid ZIP code – five digits only!"
echo "Enter a five-digit ZIP code: "
read ZIP
done
echo "Thank you"
The egrep command from the previous example is a bit tricky. To begin with, the regular expression pattern is matching a value that is exactly five digits. The -v option is used to return a value if the pattern is not found. So, if $ZIP contains a valid five-digit number, then egrep returns a false result because it is trying to find lines that don’t contain a five-digit number. The egrep command returns a true result if the $ZIP contains something besides a five-digit number.
Why the > /dev/null 2>&1? Because you do not want to display anything from the egrep command, just make use of its true/false return value. All operating system (OS) commands return a true or false value (technically each returns 0 for “true” and a positive number for false) when executed, and that is what is needed here. Any STDOUT (regular command output) or STDERR (command error messages) from the command is unnecessary and only serves to confuse matters if it is displayed to the user.
A for loop enables you to perform an operation on a set of items. For example, the following command, when run as the root user, will create five user accounts:
for person in bob ted sue nick fred
do
useradd $person
done
Like most languages, BASH scripting provides a way to prematurely exit a loop or to stop the current iteration of a loop and start a new iteration of a loop. Use the break command to immediately exit a while, until, or for loop. Use the continue command to stop the current iteration of a while, until, or for loop and start the next iteration of the loop.
A case statement is designed for when you want to perform multiple conditional checks. Although you could use an if statement with multiple elif statements, the syntax of if/elif/else is often more cumbersome than a case statement.
Here is the syntax for a case statement (note that cmd represents any BASH command or programming statement):
case var in
cond1) cmd
cmd;;
cond2) cmd
cmd;;
esac
For the previous syntax example, var represents a variable’s value that you want to conditionally check. For example, consider the following code:
name="bob" case $name in ted) echo "it is ted";; bob) echo "it is bob";; *) echo "I have no idea who you are" esac
The “condition” is a pattern that uses the same matching rules as file wildcards. An asterisk (*) matches zero or more of any character, a ? matches a single character, and you can use square brackets to match a single character of a specific range. You can also use a pipe (|) character to represent “or.” For example, consider Example 15-3, which is used to check a user’s answer to a question.
Example 15-3 Example of a case Statement
answer=yes case $answer in y|ye[sp]) echo "you said yes";; n|no|nope) echo "you said no";; *) echo "bad response";; esac
The example in Example 15-3 is a bit of a puzzle because it is intended to check user input. However, the variable is hard-coded. It would make more sense to use actual user input, which can be gathered by the read statement:
read answer
The read statement will prompt the user to provide information and read that data (technically from STDIN, which is where the program reads data from and is set to the user’s keyboard by default) into a variable that is specified as the argument to the read statement. See Example 15-4 for an example.
Example 15-4 Example of a read Statement
read answer case $answer in y|ye[sp]) echo "you said yes";; n|no|nope) echo "you said no";; *) echo "bad response";; esac
To assign values to different variables, use the following syntax:
read var1 var2 var3
Use the -p option to issue a prompt to the user:
read -p "Enter your name" name
Command substitution is the process of executing a subcommand within a larger command. It is typically used to gather data and store this data into a variable. For example, the following command stores the output of the date command into the $today variable:
today=$(date)
Command substitution can be performed by using one of two methods:
• Method 1: $(cmd)
• Method 2: `cmd`
Note that Method 2 uses backquote characters (also called “backtick” characters), not single-quote characters. Both methods yield the same results; however, Method 1 is generally considered more “readable” because it is difficult to see the difference between single-quote and backquote characters.
Want to learn more about creating BASH scripts? The following are good resources for additional information:
• man bash: The man page for the BASH shell has a great deal of information about writing BASH scripts.
• http://tldp.org: A website that is (sadly) mostly out of date. However, there is one gem of a document called the “Advanced Bash-Scripting Guide.” Click the “Guides” link under the “Documents” section and scroll down until you see this guide. The author of this guide normally updates it on a regular basis. Since the guides are listed by publication date, this guide is almost always at the top of the list.
A key feature of automation is the ability to batch commands into a single executable file. Scripting languages, such as Python, Perl, and BASH scripting, provide this powerful feature. In this chapter, you learned the basics of creating scripts, with a primary focus on BASH scripting.
1. The _____ permission must be added to a script before it can be run like a program.
2. Which command is used to display messages?
a. print
b. display
c. show
d. -echo
3. A while statement is required to have a then statement directly following which of these? (Choose two.)
a. if
b. fi
c. else
d. -elif
4. The _____ operator is used to determine if one integer is less than or equal to another integer.
5. The _____ command gathers user input and stores the value that the user types into a variable.