Shell programming can be accomplished by directly executing shell commands at the shell prompt or by storing them in the order of execution, in a text file, called a shell script, a command-line interpreter (CLI) . To execute, simply write the shell script file name, once the file has execute permission (chmod +x filename).
There is different types of shells, such as sh, bash, zsh etc.
The Bourne shell (sh) is regarded as the first UNIX shell ever. The Bourne shell has some major drawbacks, for example it doesn't have in-built functionality to handle logical and arthmetic operations.
More popularly known as the Bash shell, the GNU Bourne-Again shell (bash) was designed to be compatible with the Bourne shell. It allows us to automatically recall previously used commands and edit them with help of arrow keys, unlike the Bourne shell.
The Z Shell (zsh) is a sh shell extension with more improvements for customization, including some features of Bash.
We will discusses shell programming in general with focus on Bash shell as the main shell interpreter.
Hello, Work!
It is common to name the shell script with the ".sh" extension. And the first line begins with a "sha-bang" (#!) which proclaim where the shell interpreter is located. It like this:
#!/bin/bash
or
#!/usr/bin/env bash
The latter finds bash in current env, lends you some flexibility on different systems. To add a shell command like following:
#!/bin/bash#!/usr/bin/env bash # comment# To print Hello Work!echo'Hello Work!'
And any text following the "#" is considered a comment expect "sha-bang" at first line. Excuting this shell programming by:
$chmod+xhello_work.sh$./hello_work.shHelloWork!
Variables
Basic
Shell variables are created once are assigned, case sensitive and can consist of a combination of letters and the underscore "_". A variable can contain a number, a character or a string of characters.
PRICE_PER_APPLE=5MyFirstLetters=ABCsingle_quote='Hello world!'# Won't interpolate anything.double_quote="Hello $MyFirstLetters world!"# Will interpolate.escape_special_character="Hello \$MyFirstLetters world!"# Use backslash.
Variables can be assigned with the value of a command output, called substitution. Substitution can be done by encapsulating the command with ` `(known as back-ticks) or with $(). When the script runs, it will run the command inside the $()parenthesis or ` ` and capture its output.
An array is initialized by assign space-delimited values enclosed in (). Array members need not be consecutive or contiguous. Some members of the array can be left uninitialized.
The array elements can be accessed with their numeric index. The index of the first element is 0.
echo ${my_array[3]} # orange - note that curly brackets are needed# adding another array elementmy_array[4]="carrot"# value assignment without a $ and curly brackets# Double quote array expansions to avoid re-splitting elements.echo"${my_array[@]}"# => apple banana Fruit Basket carrotecho ${#my_array[@]} # => 5echo ${my_array[${#my_array[@]}-1]} # => carrot
Special variables
Here are some special variables in shell:
$0 - The filename of the current script.
$n - The Nth argument passed to script was invoked or function was called.
$# - The number of argument passed to script or function.
$@ - All arguments passed to script or function.
$* - All arguments passed to script or function.
$? - The exit status of the last command executed.
$$ - The process ID of the current shell. For shell scripts, this is the process ID under which they are executing.
$! - The process number of the last background command.
# => $ ./variables.sh 1 "2 2" 3echo"Script Name: $0"# => Script Name: ./variables.shecho"Second arg: $2"# => Second arg: 2 2echo"Arg Num: $#"# => Arg Num: 3echo"Exit status: $?"# => Exit status: 0echo"The process ID: $$"# => The process ID: 4589echo"As a separate argument: $@"# => As a separate argument: 1 2 2 3echo"As a one argument: $*"# => As a one argument: 1 2 2 3
The following shows the defference between $@ and $*:
To calculate simple arithmetics on variables should use: $((expression))
A=3B=$((100* $A +5)) # 305
The basic operators include:
a + b addition (a plus b)
a - b substraction (a minus b)
a * b multiplication (a times b)
a / b division (integer) (a divided by b)
a % b modulo (the integer remainder of a divided by b)
a ****** b exponentiation (a to the power of b)
String
Length
STRING="this is a string"echo"length STRING: ${#STRING}"# => length STRING: 16
Extraction Extract substring of length $LEN from $STRING starting after position $POS:
STRING="this is a string"POS=0LEN=4echo ${STRING:$POS:$LEN} # => this
If $LEN is omitted, extract substring from $POS to end of line:
STRING="this is a string"echo ${STRING:10} # => thisecho ${STRING:(-6)} # => this
Replacement Replace all/firest/beginning/end or delete occurrences of substring:
REPLACE_STRING="to be or not to be"echo"Replace first: ${REPLACE_STRING[*]/be/eat}"# => to eat or not to beecho"Replace all: ${REPLACE_STRING[*]//be/eat}"# => to eat or not to eatecho"Delete: ${REPLACE_STRING[*]// not}"# => to be or to beecho"Replace beginning: ${REPLACE_STRING[*]/%to be/to eat}"# => to be or not to eatecho"Replace end: ${REPLACE_STRING[*]/#to be/to eat}"# => to eat or not to be
File Test
Shell provide you with several useful commands to do some file tests on file system. The file test operators are mostly used in the if clause. The syntax likes below:
# file test operatorsif [ -e"function.sh" ]; then# => or if [ ! -e "function.sh" ];echo"function.sh is exist!"# => function.sh is exist!fi
Common options are as follows:
-e: True if the file exists.
-d: True if the file exists and is a directory.
-f: True if the file exists and is a regular file.
-r: True if the file exists and is readable.
-w: True if the file exists and is writable.
-x: True if the file exists and is executable.
Expression
Comparisons
Number
comparisonEvaluatedtotruewhen$a -lt $b $a < $b$a -gt $b $a > $b$a -le $b $a <= $b$a -ge $b $a >= $b$a -eq $b $a is equal to $b$a -ne $b $a is not equal to $b
The expression used by the conditional construct is evaluated to either true or false. The expression can be a single string or variable. A empty string or a string consisting of spaces or an undefined variable name, are evaluated as false.
Logical combination The expression can be a logical combination of comparisons, negation is denoted by !, ogical AND (conjunction) is &&, logical OR (disjunction) is ||. Conditional expressions should be surrounded by double brackets [[ ]]:
Background As we know, under normal circumstances every Linux program has three streams opened when it starts; one for input; one for output; and one for printing diagnostic or error messages. The three as special file descriptors, which are represented by:
stdin: 0
stdout: 1
stderr: 2
By default, stdout and stderr are printed to your terminal, that's why we can see them at all. > operator can help us redirect them to a other file descriptor. For example:
$echo"hello work!"1>hello# default stdstream is stdout, '1>' equal `>`$cathellohello
Redirect to stdstream use >&, such as: echo "hello work!" 1>&2. We can referene below to understand stdstream, to avoid stderr print to terminal, I redirect stderr to /dev/null:
echo"out stdout: $(out2>/dev/null)"# => out stdout: Standard outputecho"err stdout: $(err2>/dev/null)"# => err stdout:
Pipes
Pipelines is a way to chain commands and connect stdout from one command to the stdin of the next. By default pipelines redirects standard output only. A pipeline is represented by the pipe character: |. To show pipes, we use a command read,which will help us read from stdin:
functionisNull() {readin# rean from stdin and assign to $inif [[ ${#in} -eq0 ]]; thenecho"True"elseecho"False"fi}
Shell builtin commands are commands that can be executed within the running shell's process. Most builtin commands will be used directly. Some time Shell will use external commands, for example echo. Under macOS echo just support -n option. The following shows how to use builtin echo: