Notes, Shell Script
Last Modified: 2017-08-11 11:18:00
Acknowledge
- What is shell script?
A shell script is a file containing a series of commands.
The first shell script
hello_world.sh
#!/bin/bash
# The line above is called a shebang.
# Every shell script should include this as its first line.
# This is our first script.
echo 'Hello World!'
- make it executable
```shell
ls -l hello_world
-rw-r–r– 1 me me 63 2009-03-07 10:10 hello_world
chmod 755 hello_world
ls -l hello_world
-rwxr-xr-x 1 me me 63 2009-03-07 10:10 hello_world
./hello_word
Hello World!
<!--- more --->
### Build a program
```shell
#!/bin/bash
# Program to output a system information page
TITLE="System Information Report For $HOSTNAME"
CURRENT_TIME=$(date +"%x %r %Z")
TIME_STAMP="Generated $CURRENT_TIME, by $USER"
echo "<HTML>
<HEAD>
<TITLE>$TITLE</TITLE>
</HEAD>
<BODY>
<H1>$TITLE</H1>
<P>$TIME_STAMP</P>
</BODY>
</HTML>"
here documents
command << token
text
token
- By default, single and double quotes within here documents lose their special meaning to the shell.
- 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.
#!/bin/bash # Program to output a system information page TITLE="System Information Report For $HOSTNAME" CURRENT_TIME=$(date +"%x %r %Z") TIME_STAMP="Generated $CURRENT_TIME, by $USER" cat << _EOF_ <HTML> <HEAD> <TITLE>$TITLE</TITLE> </HEAD> <BODY> <H1>$TITLE</H1> <P>$TIME_STAMP</P> </BODY> </HTML> _EOF_
Top-down design
shell function
function name {
commands
return
}
and
name () {
commands
return
}
local variable
#!/bin/bash
i=0
function hello {
local i=1
echo 'local i = '$i
echo 'Hello World'
}
hello
echo 'global i = '$i
keep program runnable
#!/bin/bash
# Program to output a system information page
TITLE="System Information Report For $HOSTNAME"
CURRENT_TIME=$(date +"%x %r %Z")
TIME_STAMP="Generated $CURRENT_TIME, by $USER"
report_uptime () {
echo "Function report_uptime executed."
return
}
report_disk_space () {
echo "Function report_disk_space executed."
return
}
report_home_space () {
echo "Function report_home_space executed."
return
}
cat << _EOF_
<HTML>
<HEAD>
<TITLE>$TITLE</TITLE>
</HEAD>
<BODY>
<H1>$TITLE</H1>
<P>$TIME_STAMP</P>
$(report_uptime)
$(report_disk_space)
$(report_home_space)
</BODY>
</HTML>
_EOF_
############################################
./sys_info_page
<HTML>
<HEAD>
<TITLE>System Information Report For linuxbox</TITLE>
</HEAD>
<BODY>
<H1>System Information Report For linuxbox</H1>
<P>Generated 03/20/2009 05:17:26 AM EDT, by me</P>
Function report_uptime executed.
Function report_disk_space executed.
Function report_home_space executed.
</BODY>
</HTML>
control: if branching
if statement
if commands; then
commands
[elif commands; then
commands...]
[else
commands]
fi
exit status
ls -d /usr/bin
/usr/bin
echo $?
0
ls -d /bin/usr
ls: cannot access /bin/usr: No such file or directory
echo $?
2
test expression
The test command returns an exit status of zero when the expression is true and a status of one when the expression is false.
test expression
# the more popular one:
[ expression ]
test file expression
test_file () {
# test-file: Evaluate the status of a file
FILE=~/.bashrc
if [ -e "$FILE" ]; then
if [ -f "$FILE" ]; then
echo "$FILE is a regular file."
fi
if [ -d "$FILE" ]; then
echo "$FILE is a directory."
fi
if [ -r "$FILE" ]; then
echo "$FILE is readable."
fi
if [ -w "$FILE" ]; then
echo "$FILE is writable."
fi
if [ -x "$FILE" ]; then
echo "$FILE is executable/searchable."
fi
else
echo "$FILE does not exist"
return 1
fi
}
test string expression
Expression | Is Ture 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 or string1 == string2 | string1 and string2 are equal. Single or double equal signs may be used, but the use of double equal signs is greatly preferred. |
string1 != string2 | string1 and string2 are not equal. |
string1 > string2 | sting1 sorts after string2. |
string1 < string2 | string1 sorts before string2. |
test integer expression
Expression | Is Ture 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. |
compound command, enhanced test expression
[[ expression ]]
: it is similar to test and it supports all of its expressions.
# but add a new string expression
# which returns true if string is matched by the extended regular expression regex
string =~ regex
#!/bin/bash
# test-integer2: evaluate the value of an integer.
INT=-5
if [[ "$INT" =~ ^-?[0-9]+$ ]]; then
if [ $INT -eq 0 ]; then
echo "INT is zero."
else
if [ $INT -lt 0 ]; then
echo "INT is negative."
else
echo "INT is positive."
fi
if [ $((INT % 2)) -eq 0 ]; then
echo "INT is even."
else
echo "INT is odd."
fi
fi
else
echo "INT is not an integer." >&2
exit 1
fi
Another added feature of [[ ]]
is that the == operator supports pattern matching the same way pathname expansion does. For example:
FILE=foo.bar
if [[ $FILE == foo.* ]]; then
> echo "$FILE matches pattern 'foo.*'"
> fi
foo.bar matches pattern 'foo.*'
(( arithmetic expression ))
: it is used to perform arithmetic truth tests.
#!/bin/bash
# test-integer2a: evaluate the value of an integer.
INT=-5
if [[ "$INT" =~ ^-?[0-9]+$ ]]; then
if ((INT == 0)); then
echo "INT is zero."
else
if ((INT < 0)); then
echo "INT is negative."
else
echo "INT is positive."
fi
if (( ((INT % 2)) == 0)); then
echo "INT is even."
else
echo "INT is odd."
fi
fi
else
echo "INT is not an integer." >&2
exit 1
fi
combine expressions
Logical Operators
Operation | test | [[ ]] and (( )) |
---|---|---|
AND | -a | && |
OR | -o | || |
NOT | ! | ! |
#!/bin/bash
# test-integer3: determine if an integer is within a
# specified range of values.
MIN_VAL=1
MAX_VAL=100
INT=50
if [[ "$INT" =~ ^-?[0-9]+$ ]]; then
if [[ INT -ge MIN_VAL && INT -le MAX_VAL ]]; then
echo "$INT is within $MIN_VAL to $MAX_VAL."
else
echo "$INT is out of range."
fi
else
echo "INT is not an integer." >&2
exit 1
fi
We also include parentheses around the expression, for grouping
if [ ! \( $INT -ge $MIN_VAL -a $INT -le $MAX_VAL \) ]; then
echo "$INT is outside $MIN_VAL to $MAX_VAL."
else
echo "$INT is in range."
fi
two control operators, can perform branching
command1 && command2
command1 || command2
mkdir temp && cd temp
[ -d temp ] || mkdir temp
[ -d temp ] || exit 1
detect permission with if
report_home_space () {
if [[ $(id -u) -eq 0 ]]; then
cat <<- _EOF_
<H2>Home Space Utilization (All Users)</H2>
<PRE>$(du -sh /home/*)</PRE>
_EOF_
else
cat <<- _EOF_
<H2>Home Space Utilization ($USER)</H2>
<PRE>$(du -sh $HOME)</PRE>
_EOF_
fi
return
}
Keyboard input
read [-options] [variable...]
- If read receives fewer than the expected number, the extra variables are empty.
- If read receives more than the expected number, the final variable will contain all of the extra input.
- If no variables are listed after the read command, a shell variable,
REPLY
, will be assigned all the input.
#!/bin/bash
# read-secret: input a secret pass phrase
if read -t 10 -sp "Enter secret pass phrase > " secret_pass; then
echo "\nSecret pass phrase = '$secret_pass'"
else
echo "\nInput timed out" >&2
exit 1
fi
IFS (Internal Field Separator)
The shell allows one or more variable assignments to take place immediately before a command.
These assignments alter the environment for the command that follows.
The effect of the assignment is temporary; only changing the environment for the duration of the command.
In our case, the value of IFS is changed to a colon character.
#!/bin/bash
# read-ifs: read fields from a file
FILE=/etc/passwd
read -p "Enter a user name > " user_name
file_info=$(grep "^$user_name:" $FILE)
if [ -n "$file_info" ]; then
IFS=":" read user pw uid gid name home shell <<< "$file_info"
echo "User = '$user'"
echo "UID = '$uid'"
echo "GID = '$gid'"
echo "Full Name = '$name'"
echo "Home Dir. = '$home'"
echo "Shell = '$shell'"
else
echo "No such user '$user_name'" >&2
exit 1
fi
The <<<
operator indicates a here string.
A here string is like a here document, only shorter, consisting of a single string.
We might wonder why this rather oblique method was chosen rather than:
echo "$file_info" | IFS=":" read user pw uid gid name home shell
You Can’t Pipe read
The explanation has to do with the way the shell handles pipelines.
In bash (and other shells such as sh), pipelines create subshells (subshells is the subprocesses).
Subshells in Unix-like systems create copies of the environment for the processes to use while they execute.
When the command exits, the subshell and its environment are destroyed.
This means that a subshell can never alter the environment of its parent process.
Then the effect of the assignment is lost.
validating input
echo "Invalid input '$REPLY'" >&2
exit 1
}
read -p "Enter a single item > "
# input is empty (invalid)
[[ -z $REPLY ]] && invalid_input
# input is multiple items (invalid)
(( $(echo $REPLY | wc -w) > 1 )) && invalid_input
# is input a valid filename?
if [[ $REPLY =~ ^[-[:alnum:]\._]+$ ]]; then
echo "'$REPLY' is a valid filename."
if [[ -e $REPLY ]]; then
echo "And file '$REPLY' exists."
else
echo "However, file '$REPLY' does not exist."
fi
# is input a floating point number?
if [[ $REPLY =~ ^-?[[:digit:]]*\.[[:digit:]]+$ ]]; then
echo "'$REPLY' is a floating point number."
else
echo "'$REPLY' is not a floating point number."
fi
# is input an integer?
if [[ $REPLY =~ ^-?[[:digit:]]+$ ]]; then
echo "'$REPLY' is an integer."
else
echo "'$REPLY' is not an integer."
fi
else
echo "The string '$REPLY' is not a valid filename."
fi
menu
#!/bin/bash
# read-menu: a menu driven system information program
clear
echo "
Please Select:
1. Display System Information
2. Display Disk Space
3. Display Home Space Utilization
0. Quit
"
read -p "Enter selection [0-3] > "
if [[ $REPLY =~ ^[0-3]$ ]]; then
if [[ $REPLY == 0 ]]; then
echo "Program terminated."
exit
fi
if [[ $REPLY == 1 ]]; then
echo "Hostname: $HOSTNAME"
uptime
exit
fi
if [[ $REPLY == 2 ]]; then
df -h
exit
fi
if [[ $REPLY == 3 ]]; then
if [[ $(id -u) -eq 0 ]]; then
echo "Home Space Utilization (All Users)"
du -sh /home/*
else
echo "Home Space Utilization ($USER)"
du -sh $HOME
fi
exit
fi
else
echo "Invalid entry." >&2
exit 1
fi
Flow control: while/until loop
while
while commands; do commands; done
break
and continue
#!/bin/bash
# while-menu2: a menu driven system information program
DELAY=3 # Number of seconds to display results
while true; do
clear
cat <<- _EOF_
Please Select:
1. Display System Information
2. Display Disk Space
3. Display Home Space Utilization
0. Quit
_EOF_
read -p "Enter selection [0-3] > "
if [[ $REPLY =~ ^[0-3]$ ]]; then
if [[ $REPLY == 1 ]]; then
echo "Hostname: $HOSTNAME"
uptime
sleep $DELAY
continue
fi
if [[ $REPLY == 2 ]]; then
df -h
sleep $DELAY
continue
fi
if [[ $REPLY == 3 ]]; then
if [[ $(id -u) -eq 0 ]]; then
echo "Home Space Utilization (All Users)"
du -sh /home/*
else
echo "Home Space Utilization ($USER)"
du -sh $HOME
fi
sleep $DELAY
continue
fi
if [[ $REPLY == 0 ]]; then
break
fi
else
echo "Invalid entry."
sleep $DELAY
fi
done
echo "Program terminated."
until
#!/bin/bash
# until-count: display a series of numbers
count=1
until [ $count -gt 5 ]; do
echo $count
count=$((count + 1))
done
echo "Finished."
process files with while or until loop
- redirection
#!/bin/bash # while-read: read lines from a file while read distro version release; do printf "Distro: %s\tVersion: %s\tReleased: %s\n" \ $distro \ $version \ $release done < distros.txt
- pipe
#!/bin/bash # while-read2: read lines from a file sort -k 1,1 -k 2n distros.txt | while read distro version release; do printf "Distro: %s\tVersion: %s\tReleased: %s\n" \ $distro \ $version \ $release done
Stay out of trouble
syntactic errors
if [ $number = 1 ]; then echo "Number is equal to 1."
# add double quotes to prevent unexpected expansion
if [ "$number = 1]; the echo "Number is equal to 1."
logical errors
- incorrect conditional expressions.
- “Off by one” errors.
- Unanticipated situations.
testing
# use 'echo' to show the expanded parameters
echo rm * # TESTING
debugging
- isolate the area related to problem
commenting out the code sections the code - tracing
to activate tracing for the entire script by adding the -x option to the first line
number=1
if [ $number = 1 ]; then
echo “Number is equal to 1.”
else
echo “Number is not equal to 1.”
fi
##############################################
export PS4=’$LINENO + ‘
trouble
5 + number=1
7 + ‘[‘ 1 = 1 ‘]’
8 + echo ‘Number is equal to 1.’
Number is equal to 1.
```shell
#!/bin/bash
# trouble: script to demonstrate common errors
# to perform a trace on a selected portion of a script
# we can use the set command with the -x option:
number=1
set -x # Turn on tracing
if [ $number = 1 ]; then
echo "Number is equal to 1."
else
echo "Number is not equal to 1."
fi
set +x # Turn off tracing
Flow control: case branching
case word in
[pattern [| pattern]...) commands ;;]...
esac
The patterns used by case are the same as those used by pathname expansion. Here are some valid patterns:a)
: Matches if word equals “a”.[[:alpha:]])
: Matches if word is a single alphabetic character.???)
: Matches if word is exactly three characters long.*.txt)
: Matches if word ends with the characters “.txt”.*)
: Matches any value of word. It is good practice to include this as the last pattern in a case command; that is, to catch any possible invalid values.
#!/bin/bash
# case-menu: a menu driven system information program
clear
echo "
Please Select:
1. Display System Information
2. Display Disk Space
3. Display Home Space Utilization
0. Quit
"
read -p "Enter selection [0-3] > "
case $REPLY in
0) echo "Program terminated."
exit
;;
1) echo "Hostname: $HOSTNAME"
uptime
;;
2) df -h
;;
3) if [[ $(id -u) -eq 0 ]]; then
echo "Home Space Utilization (All Users)"
du -sh /home/*
else
echo "Home Space Utilization ($USER)"
du -sh $HOME
fi
;;
*) echo "Invalid entry" >&2
exit 1
;;
esac
match more the one test
In bash prior to version 4.0 there was no way for case to match more than one test.
Modern versions of bash, add the ;;&
notation to terminate each action, we can do this:
#!/bin/bash
# case4-2: test a character
read -n 1 -p "Type a character > "
echo
case $REPLY in
[[:upper:]]) echo "'$REPLY' is upper case." ;;&
[[:lower:]]) echo "'$REPLY' is lower case." ;;&
[[:alpha:]]) echo "'$REPLY' is alphabetic." ;;&
[[:digit:]]) echo "'$REPLY' is a digit." ;;&
[[:graph:]]) echo "'$REPLY' is a visible character." ;;&
[[:punct:]]) echo "'$REPLY' is a punctuation symbol." ;;&
[[:space:]]) echo "'$REPLY' is a whitespace character." ;;&
[[:xdigit:]]) echo "'$REPLY' is a hexadecimal digit." ;;&
esac
#####################################################################
case4-2
Type a character > a
'a' is lower case.
'a' is alphabetic.
'a' is a visible character.
'a' is a hexadecimal digit.
Positional parameters
access to the contents of the command line
#!/bin/bash
# posit-param: script to view command line parameters
echo "
Number of arguments: $#
\$0 = $0
\$1 = $1
\$2 = $2
"
#####################################################
posit-param
Number of arguments: 0
$0 = /home/me/bin/posit-param
$1 =
$2 =
#####################################################
Number of arguments: 2
posit-param a b
$0 = /home/me/bin/posit-param
$1 = a
$2 = b
shift, access to a large number of arguments
Each time shift is executed, the value of $2
is moved to $1
, the value of $3
is moved to $2
and so on.
The value of $#
is also reduced by one.
In addition to $0
, which never changes.
#!/bin/bash
# posit-param2: script to display all arguments
count=1
while [[ $# -gt 0 ]]; do
echo "Argument $count = $1"
count=$((count + 1))
shift
done
#####################################################
posit-param2 a b c d
Argument 1 = a
Argument 2 = b
Argument 3 = c
Argument 4 = d
group positional parameters
“$@”
is by far the most useful for most situations, because it preserves the integrity of each positional parameter.
#!/bin/bash
# posit-params3 : script to demonstrate $* and $@
print_params () {
echo "\$1 = $1"
echo "\$2 = $2"
echo "\$3 = $3"
echo "\$4 = $4"
}
pass_params () {
echo -e "\n" '$* :'; print_params $*
echo -e "\n" '"$*" :'; print_params "$*"
echo -e "\n" '$@ :'; print_params $@
echo -e "\n" '"$@" :'; print_params "$@"
}
pass_params "word" "words with spaces"
#####################################################
posit-param3
$* :
$1 = word
$2 = words
$3 = with
$4 = spaces
"$*" :
$1 = word words with spaces
$2 =
$3 =
$4 =
$@ :
$1 = word
$2 = words
$3 = with
$4 = spaces
"$@" :
$1 = word
$2 = words with spaces
$3 =
$4 =
# with our arguments
both $* and $@ produce a four word result:
word words with spaces
"$*" produces a one word result:
"word words with spaces"
"$@" produces a two word result:
"word" "words with spaces"
a complicated application
- Output file
-f or --file
- Interactive mode
-i or --interactive
- Help
-h or --help
#!/bin/bash # sys_info_page: program to output a system information page PROGNAME=$(basename $0) TITLE="System Information Report For $HOSTNAME" CURRENT_TIME=$(date +"%x %r %Z") TIMESTAMP="Generated $CURRENT_TIME, by $USER" report_uptime () { cat <<- _EOF_ <H2>System Uptime</H2> <PRE>$(uptime)</PRE> _EOF_ return } report_disk_space () { cat <<- _EOF_ <H2>Disk Space Utilization</H2> <PRE>$(df -h)</PRE> _EOF_ return } report_home_space () { if [[ $(id -u) -eq 0 ]]; then cat <<- _EOF_ <H2>Home Space Utilization (All Users)</H2> <PRE>$(du -sh /home/*)</PRE> _EOF_ else cat <<- _EOF_ <H2>Home Space Utilization ($USER)</H2> <PRE>$(du -sh $HOME)</PRE> _EOF_ fi return } usage () { echo "$PROGNAME: usage: $PROGNAME [-f file | -i]" return } write_html_page () { cat <<- _EOF_ <HTML> <HEAD> <TITLE>$TITLE</TITLE> </HEAD> <BODY> <H1>$TITLE</H1> <P>$TIMESTAMP</P> $(report_uptime) $(report_disk_space) $(report_home_space) </BODY> </HTML> _EOF_ return } # process command line options interactive= filename= while [[ -n $1 ]]; do case $1 in -f | --file) shift filename=$1 ;; -i | --interactive) interactive=1 ;; -h | --help) usage exit ;; *) usage >&2 exit 1 ;; esac shift done # interactive mode if [[ -n $interactive ]]; then while true; do read -p "Enter name of output file: " filename if [[ -e $filename ]]; then read -p "'$filename' exists. Overwrite? [y/n/q] > " case $REPLY in Y|y) break ;; Q|q) echo "Program terminated." exit ;; *) continue ;; esac fi done fi # output html page if [[ -n $filename ]]; then if touch $filename && [[ -f $filename ]]; then write_html_page > $filename else echo "$PROGNAME: Cannot write file '$filename'" >&2 exit 1 fi else write_html_page fi
control: for loop
the original for command’s syntax
for variable [in words]; do
commands
done
#!/bin/bash
# longest-word : find longest string in a file
while [[ -n $1 ]]; do
if [[ -r $1 ]]; then
max_word=
max_len=0
for i in $(strings $1); do
len=$(echo $i | wc -c)
if (( len > max_len )); then
max_len=$len
max_word=$i
fi
done
echo "$1: '$max_word' ($max_len characters)"
fi
shift
done
If the optional in words portion of the for command is omitted, for defaults to processing the positional parameters.
# test.sh
#!/bin/bash
for i; do
echo $i
done
####################
test 1 2 3
1
2
3
C style
for (( expression1; expression2; expression3 )); do
commands
done
#!/bin/bash
# simple_counter : demo of C style for command
for (( i=0; i<5; i=i+1 )); do
echo $i
done
################################################
simple_counter
0
1
2
3
4
Strings and numbers
omitted
Strings and number
Arrays
- shell arrays is
0-based
. - one way to create an array
declare -a a
usually in the following way
name[subscript]=value name=(value1 value2 ...) days=(Sun Mon Tue Wed Thu Fri Sat) days=([0]=Sun [1]=Mon [2]=Tue [3]=Wed [4]=Thu [5]=Fri [6]=Sat)
output the whole array
(a small mistake in the book, corrected)$ animals=("a dog" "a cat" "a fish") for i in ${animals[*]}; do echo $i; done a dog a cat a fish for i in ${animals[@]}; do echo $i; done a dog a cat a fish for i in "${animals[*]}"; do echo $i; done a dog a cat a fish for i in "${animals[@]}"; do echo $i; done a dog a cat a fish
determine the number of elements
a[100]=foo echo ${#a[@]} # number of array elements 1 echo ${#a[100]} # length of element 100 3
find the index of array used
foo=([2]=a [4]=b [6]=c) for i in "${foo[@]}"; do echo $i; done a b c for i in "${!foo[@]}"; do echo $i; done 2 4 6
sort the array
#!/bin/bash # array-sort : Sort an array a=(f e d c b a) echo "Original array: ${a[@]}" a_sorted=($(for i in "${a[@]}"; do echo $i; done | sort)) echo "Sorted array: ${a_sorted[@]}"
delete an array
$ foo=(a b c d e f) $ echo ${foo[@]} a b c d e f $ unset foo $ echo ${foo[@]} $ ################################## $ foo=(a b c d e f) $ echo ${foo[@]} a b c d e f $ unset 'foo[2]' $ echo ${foo[@]} a b d e f
any reference to an array variable without a subscript refers to element zero of the array
$ foo=(a b c d e f) $ echo ${foo[@]} a b c d e f $ foo=A $ echo ${foo[@]} A b c d e f
associative array
associative arrays can only be created with thedeclare
command using the new-A
optiondeclare -A colors colors["red"]="#ff0000" colors["green"]="#00ff00" colors["blue"]="#0000ff" echo ${colors["blue"]}
Odds and ends
group command or subshell
```shellgroup command
the braces must be separated from the commands by a space
the last command must be terminated with either a semicolon or a newline prior to the closing brace.
{ command1; command2; [command3; …] }
subshell
(command1; command2; [command3;…])
```shell
ls -l > output.txt
echo "Listing of foo.txt" >> output.txt
cat foo.txt >> output.txt
###################################################################
{ ls -l; echo "Listing of foo.txt"; cat foo.txt; } > output.txt
(ls -l; echo "Listing of foo.txt"; cat foo.txt) > output.txt
for processes that intake standard input:
(a list of commands)
```
# to solve the problem brought by subshell, we can employ process substitution like this:
read < <(echo "foo")
echo $REPLY
- trap
trap argument signal [signal...]
#!/bin/bash
# trap-demo2 : simple signal handling demo
exit_on_signal_SIGINT () {
echo "Script interrupted." 2>&1
exit 0
}
exit_on_signal_SIGTERM () {
echo "Script terminated." 2>&1
exit 0
}
trap exit_on_signal_SIGINT SIGINT
trap exit_on_signal_SIGTERM SIGTERM
for i in {1..5}; do
echo "Iteration $i of 5"
sleep 5
done
temp file
mktemp
tempfile=$(mktemp /tmp/foobar.$$.XXXXXXXXXX) echo $tempfile /tmp/foobar.6593.UOZuvM6654
asynchronous execution
wait
#!/bin/bash
# async-parent : Asynchronous execution demo (parent)
echo "Parent: starting..."
echo "Parent: launching child script..."
async-child &
pid=$!
echo "Parent: child (PID= $pid) launched."
echo "Parent: continuing..."
sleep 2
echo "Parent: pausing to wait for child to finish..."
wait $pid
echo "Parent: child is finished. Continuing..."
echo "Parent: parent is done. Exiting."
#!/bin/bash
# async-child : Asynchronous execution demo (child)
echo "Child: child is running..."
sleep 5
echo "Child: child is done. Exiting."
async-parent
Parent: starting...
Parent: launching child script...
Parent: child (PID= 6741) launched.
Parent: continuing...
Child: child is running...
Parent: pausing to wait for child to finish...
Child: child is done. Exiting.
Parent: child is finished. Continuing...
Parent: parent is done. Exiting.
- named pipe
# it works as `process1 | process2` process1 > named_pipe process2 < named_pipe
mkfifo
mkfifo pipe1
# process1
ls -l > pipe1
# process2
cat < pipe1
Summay
Well, we have completed our journey. The only thing left to do now is practice, practice, practice. Even though we covered a lot of ground in our trek, we barely scratched the surface as far as the command line goes. There are still thousands of command line programs left to be discovered and enjoyed. Start digging around in /usr/bin
and you’ll see!