Brief Introduction to Shell Script
Contents
This article is mainly refered to “The Linux Command Line”1. I just take some most important things out of the book.
Expansion
Each time you type a command line and press the Enter key, bash performs several processes upon the text before it carries out your command. Just look an example:
|
|
Why not display an asterisk? That’s expansion! *
expands to all files in
current directory.
Pathname expansion
|
|
Tilde expansion
As you know, ~
has a special meaning of the home directory of current user.
|
|
Arithmetic expansion
The shell allows arithmetic to be performed by expansion. This allows us to use the shell prompt as a calculator:
|
|
Note: Arithmetic expansion supports only integers (whole numbers, no decimals).
Brace expansion
|
|
Patterns to be brace expanded may contain a leading portion called a preamble and a trailing portion called a postscript. The brace expression itself may contain either a comma-separated list of strings or a range of integers or single characters. The pattern may not contain embedded whitespace. Here is an example using a range of integers:
|
|
Command substitution
Command substitution allows us to use the output of a command as an expansion:
|
|
There is an alternative syntax for command substitution in older shell programs that is also supported in bash . It uses back quotes instead of the dollar sign and parentheses:
|
|
Quoting
Usage of single or double quotes in shell commands is confusing for me, at least. Hey, take a look:
|
|
Isn’t that confusing? Fortunately, the shell provides a mechanism called quoting to selectively suppress unwanted expansions.
Double quotes
If you place text inside double quotes, all the special characters used by the shell lose their special meaning and are treated as ordinary characters. The exceptions are $ (dollar sign), \ (backslash), and ` (back tick).
By default, word splitting looks for the presence of spaces, tabs, and newlines (linefeed characters) and treats them as delimiters between words. This means that unquoted spaces, tabs, and newlines are not considered to be part of the text. They serve only as separators. The fact that newlines are considered delimiters by the word splitting mechanism causes an interesting, albeit subtle, effect on command substitution. Consider the following:
|
|
In the first instance, the unquoted command substitution resulted in a command line containing 38 arguments; in the second, the result was a command line with 1 argument that includes the embedded spaces and newlines.
Single quotes
If we need to suppress all expansions, we use single quotes. Here is a comparison of unquoted, double quotes, and single quotes:
|
|
As we can see, with each succeeding level of quoting, more and more expansions are suppressed.
Variables and assignments
- the shell does not care about the type of data assigned to a variable; it treats them all as strings.
- in an assignment, there must be no spaces between the variable name, the equal sign, and the value.
|
|
“Here documents”
A here document is an additional form of I/O redirection in which we embed a body of text into our script and feed it into the standard input of a command. It works like this:
|
|
where command is the name of a command that accepts standard input and token is a string used to indicate the end of the embedded text. For example, you
|
|
Namely, you just use a custom EOF indicator of the input to that command. So what’s the advantage of using a here document? It’s mostly the same as echo , except that, by default, single and double quotes within here documents lose their special meaning to the shell. Here is a command-line example:
|
|
As we can see, the shell pays no attention to the quotation marks. It treats them as ordinary characters. This allows us to embed quotes freely within a here document.
If we change the redirection operator from <<
to <<-
, the shell will
ignore leading tab characters in the here document. This allows a here
document to be indented, which can improve readability:
|
|
Flow control
Using if
The syntax of if-statement is
|
|
where commands is a list of commands. This may be a little confusing at first glance, “if” should judge a condition rather than a series of commands. This leads to the concept of exit status.
Commands (including the scripts and shell functions we write) issue a value to the system when they terminate, called an exit status. This value, which is an integer in the range of 0 to 255, indicates the success or failure of the command’s execution. By convention, a value of 0 indicates success, and any other value indicates failure. The shell provides a parameter that we can use to examine the exit status. Here we see it in action:
|
|
The shell provides two extremely simple built-in commands that do nothing except terminate with either a 0 or 1 exit status. The true command always executes successfully, and the false command always executes unsuccessfully:
|
|
Using test
By far, the command used most frequently with if is test
. The test
command performs a variety of checks and comparisons. It has two
equivalent forms:
|
|
where “expression” is an expression that is evaluated as either true or false. The test command returns an exit status of 0 when the expression is true and a status of 1 when the expression is false.
File expressions
Expression | Is true if… |
---|---|
file1 -nt file2 | file1 is newer than file2 . |
file1 -ot file2 | file1 is older than file2 . |
-d file | file exists and is a directory. |
-e file | file exists. |
-L file | file exists and is a symbolic link. |
-r file | file exists and is readable (has readable permission for the effective user). |
-s file | file exists and has a length greater than zero. |
-w file | file exists and is writable (has writable permission for the effective user). |
-x file | file exists and is executable (has execute/search permission for the effective user). |
String expressions
Expression | Is true if… |
---|---|
string | string is not null |
-n string | the length of string is greater than zero |
-z string | the length of string is zero |
string1 == string2 | string1 and string2 are equal. |
string1 != string2 | string1 and string2 are not equal. |
string1 > string2 | string1 sorts after string2 . |
string1 < string2 | string1 sorts before string2 . |
Note: the > and < expression must be quoted (or escaped with a backslash)
when used with test
, oterwise they will be interpreted as the shell
redirection opreators.
Integer expressions
Expression | Is true if… |
---|---|
integer1 -eq integer2 | integer1 is equal to integer2 |
integer1 -ne integer2 | integer1 is not equal to integer2 |
integer1 -le integer2 | integer1 is less than or equal to integer2 |
integer1 -lt integer2 | integer1 is less than integer2 |
integer1 -ge integer2 | integer1 is greater than or equal to integer2 |
integer1 -gt integer2 | integer1 is greater than integer2 |
Modern test
Recent versions of bash include a compound command that acts as an
enhanced replacement for test
. It uses the following syntax:
|
|
where expression is an expression that evaluates to either a true or false result. The [[ ]] command is very similar to test (it supports all of its expressions) but adds an important new string expression:
|
|
which returns true if string
is matched by the extended regular expression
regex
.
(( ))-Designed for integers
(( )) is used to perform arithmetic truth tests. An arithmetic truth test results in true if the result of the arithmetic evaluation is non-zero.
|
|
Shell function
Shell functions must be declared before using it. There are two ways to define a shell function:
|
|
Use local variables in shell functions
Local variables are accessible only within the shell function in which they are defined, and they cease to exist once the shell function terminates.
|
|
|
|
We see that the assignment of values to the local variable foo within both shell functions has no effect on the value of foo defined outside the functions.
TO BE CONTINUED…
“The Linux Command Line, A Complete Introduction”, William E. Shotts, Jr. ↩︎