Index
Shell Programming¶
No programming language is perfect. There is not even a single best language; there are only languages well suited or perhaps poorly suited for particular purposes. --- Herbert Mayer
When NOT to use shell scripts
- Resource-intensive tasks, especially where speed is a factor (sorting, hashing, recursion ...)
- Procedures involving heavy-duty math operations, especially floating point arithmetic, arbitrary precision calculations, or complex numbers (use C++ or FORTRAN instead)
- Cross-platform portability required (use C or Java instead)
- Complex applications, where structured programming is a necessity (type-checking of variables, function prototypes, etc.)
- Mission-critical applications upon which you are betting the future of the company
- Situations where security is important, where you need to guarantee the integrity of your system and protect against intrusion, cracking, and vandalism
- Project consists of subcomponents with interlocking dependencies
- Extensive file operations required (Bash is limited to serial file access, and that only in a particularly clumsy and inefficient line-by-line fashion.)
- Need native support for multi-dimensional arrays
- Need data structures, such as linked lists or trees
- Need to generate / manipulate graphics or GUIs
- Need direct access to system hardware
- Need port or socket I/O
- Need to use libraries or interface with legacy code
- Proprietary, closed-source applications (Shell scripts put the source code right out in the open for all the world to see.)
In the simplest case, a script is nothing more than a list of system commands stored in a file. At the very least, this saves the effort of retyping that particular sequence of commands each time it is invoked.
Example 2-1. Cleanup: A script to clean up the log files in /var/log
The most important thing for developers is to avoid duplicated work by reusing code. Therefore, developers often use variables to replace literals and functions to replace code blocks.
Example 2-2. Cleanup: An improved clean-up script
One of the good practices for coding is to validate the input and permissions of the program. When we put commands in a shell script and make the shell script executable, the shell script becomes a program.
Example 2-3. Cleanup: An enhanced and generalized version.
The comments in the example is to help understand the logic of the script. It's helpful for beginners. For experienced shell script developers, the comments are unnecessary. After removing comments, the script is clean.
Example 2-4. Cleanup: Clean version.
As you see, by adding arguments validation and permission checking, the script becomes much longer than the initial version. In practice, a shell script might contain thousands lines of code.
Shell script can be invoked by sh [path of the shell script] or
bash [path of the shell script]. By making the script executable with a
chmod command, the shell script can be executed by relative path or absolute
path. By adding the parent path of the shell script to the PATH environment
variable, the shell script can be executed by typing shell script name[ENTER]
from the command line.
Special Characters¶
# Comments¶
Lines beginning with a # (with the exception of #!) are comments and will
not be executed. There are several different situations where comments are
used.
Pipe characters | or || must be at the end of the line
The below code won't work because || is put at the beginning of the subsequent line
A quoted or escaped # in an echo statment does not begin a comment.
# is not only used for comments
- Parameter substituation
echo ${PATH#*:}(show $PATH except for the first entry) - Base conversion
echo $(( 2#101011 ))(display the number 101011 in binany) - Pattern match operation
${#var}(return the length of the var value)
; Command separator¶
Permits putting two or more commands on the same line.
;; or ;;& or ;& Terminators in a case option¶
;; is used in bash 4 or older version ;;& or ;& are supported in bash 4+
. dot¶
- The dot command - equivalent to
sourceand it's a bash builtin. - The dot as a component of a filename. a leading dot is the prefix of a hidden file.
- The dot character match and it matches a single character.
' and " Quoting¶
"is used for partial quoting - "STRING" preserves (from interpretation) most of the special characters within STRING.'is used for full quoting - 'STRING' preserves all special characters within STRING. This is a stronger form of quoting than "STRING".
, Comma operator¶
The comma operator links together a series of arithmetic operations. All are evaluated, but only the last one is returned.
The comma operator can also concatenate strings. The below example looks advanced.
,, , Lowercase conversion in parameter substitution¶
It's added in version 4 of bash.
\ Escape¶
A quoting mechanism for single characters. \X escapes the character X. This has the effect of "quoting" X, equivalent to 'X'. The may be used to quote " and ', so they are expressed literally.
/ Filename path separator¶
Separates the components of a filename (as in /home/bozo/projects/Makefile). This is also the division arithmetic operator.
` Command substitution¶
The `command` construct makes available the output of command for assignment to a variable. This is also known as backquotes or backticks.
: Null command¶
This is the shell equivalent of a "NOP" (no op, a do-nothing operation). It may be considered a synonym for the shell builtin true. The ":" command is itself a Bash builtin, and its exit status is true (0).
It can be used to construct an endless loop:
The while loop is the same as:
It can be used as a placeholder in if/else:
Provide a placeholder where a binary operation is expected:
Evaluate string of variables using parameter substitution:
In combination with the > redirection operator, truncates a file to zero length, without changing its permissions. If the file did not previously exist, creates it.
:> file.txt vs cat /dev/null > file.txt
:> file.txt has the same effect with cat /dev/null > file.txt but :> file.txt won't fork a new process since : is a builtin.
:> file.txt vs :>> file.txt
:>> file.txt has no effect if file.txt is previous-existing while :> file.txt will empty the content of file.txtif it's previous-existing.
Both will create a new empty file.txt if it didn't previously exist.
May be used to begin a comment line, although this is not recommended. Using # for a comment turns off error checking for the remainder of that line, so almost anything may appear in a comment. However, this is not the case with :.
The ":" also serves as a field separator, in /etc/passwd, and in the $PATH variable.
! reverse (or negate) the sense of a test or exit status [bang].¶
As an operator prefixing a command invokes the Bash history mechanism. It's an convenient way to call the history command starting with the text after !.
Here is an example:
Note that within a script, the history mechanism is disabled.
It also inverts the meaning of a test operator. This can, for example, change the sense of equal ( = ) to not-equal ( != ). The ! operator is a Bash keyword.
* wild card [asterisk]¶
The * character serves as a "wild card" for filename expansion in globbing. By itself, it matches every filename in a given directory.
The * also represents any number (or zero) characters in a regular expression.
In the context of arithmetic operations, the * denotes multiplication.
A double asterisk (**) can represent the exponentiation operator or extended file-match globbing.
? test operator or wild card¶
Within certain expressions, the ? indicates a test for a condition.
The ? character serves as a single-character "wild card" for filename expansion in globbing, as well as representing one character in an extended regular expression.
$ Variable substitution (Contents of a variable)¶
A $ prefixing a variable name indicates the value the variable holds.
In a regular expression, a "$" addresses the end of a line of text.
Parameter substitution: ${parameter} Same as $parameter, i.e., value of the variable parameter. In certain contexts, only the less ambiguous ${parameter} form works.
Positional parameter: $* - All of the positional parameters, seen as a single word, $@ - Same as $*, but each parameter is a quoted string, that is, the parameters are passed on intact, without interpretation or expansion. This means, among other things, that each parameter in the argument list is seen as a separate word.
Usage of $* and $@
$* must be quoted, $@ should be quoted
Exit status variable: $?. The $? variable holds the exit status of a command, a function, or of the script itself.
Process ID variable: $$. The $$ variable holds the process ID of the script in which it appears.
() Command group¶
A listing of commands within parentheses starts a subshell
Variables inside parentheses, within the subshell, are not visible to the rest of the script. The parent process, the script, cannot read variables created in the child process, the subshell.
() can also be used to initialize an array.
{} Brace expansion¶
No spaces allowed within the braces
Unless the spaces are quoted or escaped
Extended Brace expansion: {a..z}, {0..10}
Block of code {code here}. Also referred to as an inline group, this construct, in effect, creates an anonymous function (a function without a name). However, unlike in a "standard" function, the variables inside a code block remain visible to the remainder of the script.
Space should be kept after { and before }
; should be used after statement
Otherwise, it's not interpretable by bash and have to terminate by Ctrl+C
The code block enclosed in braces may have I/O redirected to and from it.
Unlike a command group within (parentheses), as above, a code block enclosed by {braces} will not normally launch a subshell.
Placeholder for text. Used after xargs -i (replace strings option). The {} double curly brackets are a placeholder for output text.
Path name which is mostly used in find command. It's not a shell builtin.
The ";" ends the -exec option of a find command sequence. It needs to be escaped to protect it from interpretation by the shell.
[] Test operator¶
Test expression between [ ]. Note that [ is part of the shell builtin test (and a synonym for it), not a link to the external command /usr/bin/test.
Test expression between [[]]. More flexible than the single-bracket [ ] test, this is a shell keyword.
Array element. In the context of an array, brackets set off the numbering of each element of that array.
Range of characters. As part of a regular expression, brackets delineate a range of characters to match.
Integer expansion. Evaluate integer expression between $[ ].
Note that this usage ($[]) is deprecated, and has been replaced by the (( ... )) construct.
(()) Integer Expansion¶
Expand and evaluate integer expression between (()).
> &> >& >> < <> Redirection¶
scriptname >filename redirects the output of scriptname to file filename. Overwrite filename if it already exists.
command &>filename redirects both the stdout and the stderr of command to filename. This is useful for suppressing output when testing for a condition. For example, let us test whether a certain command exists.
In a script, it can be used like this:
command >&2 redirects stdout of command to stderr.
scriptname >>filename appends the output of scriptname to file filename. If
filename does not already exist, it is created.
[i]<>filename opens file filename for reading and writing, and assigns file descriptor i to it. If filename does not exist, it is created.