第三十一章31-疑难排解
随着脚本越来越复杂,出故障的概率也越来越大,
本章学习几个可以跟踪和消除问题的有用技巧。31.1 语法错误
出现语法错误,绝大多数情况,shell会拒绝执行脚本
1.丢失引号,使用vi编辑器语法高亮帮助查找错误,:syntax on
2.丢失或意外的标记, 比如说丢失if后的分号,
if 会计算列表中最后一个命令的退出代码
3.意料不到的展开,例如 [$num = 1] 区别于 ["$num" = 1]
当$num 里面什么都没有时,前者会报语法错误。31.2 逻辑错误
脚本会正常执行,但是不会得到期望的结果
1.不正确的条件表达式,例如 if xxx,逻辑颠倒等
2.循环边界问题,循环语句常犯错误,过早结束循环或循环过多
3.意外情况,大多数逻辑错误来自意料之外的情况
防范措施:
1. cd $dir_name && rm * 可能的意外:dir_name变量值为空
2. [[-d $dir_name]] && cd $dir_name && rm *
持续改进:对于意外的情况,最好终止执行,提示错误信息
echo "wrong +10086" >&2
3. 验证用户输入,例如 [[$REPLY =~ ^[0-3]$ ]]31.3 测试
行业经验,早发布,常发布,早期修复bug,成本低
1.好的代码让测试更简单,使用echo 命令与注释,标记测试的改动
2.测试案例要全,比如 两数比较大小有3种情况
3.重要的功能花在测试的时间要多些if [[ -d $dir_name ]]; then
if cd $dir_name; then
echo rm * # TESTING
else
echo "cannot cd to '$dir_name'" >&2
exit 1
fi
else
echo "no such directory: '$dir_name'" >&2
exit 1
fi
exit # TESTING31.4 调试
设计良好的脚本应该具备防卫能力,能检测异常发生,给用户反馈。
1.我们可以注释代码块,让错误隔离在一个区域
2.标准错误输出>&2,看看执行顺序和时间是否正确。
3.脚本第一行#!/bin/xxx,末尾加上 -x 选项 ,进行全脚本追踪
4.在-x基础上,使用PS4操作符可以追踪显示行号
5.使用set -x [set +x] 命令 进行选定区域 追踪脚本
6.循环内部,打印变量数值往往可以发现问题原因
[seven@localhost playground]$ bash 31.1.txt
number=1
+ '[' 1 = 1 ']'
+ echo 'Number is equal to 1.'
Number is equal to 1.
+ set +x[seven@localhost playground]$ export PS4='$LINENO + '
[seven@localhost playground]$ bash 31.1.txt
6 + '[' 1 = 1 ']'
7 + echo 'Number is equal to 1.'
Number is equal to 1.
11 + set +x
第三十二章32-流程控制(case分支)
在28章我们学了if、elif语句,但是elif还不够方便,
许多编程语言额外增加了
类似于switch、case之类的流程控制机制。32.1 case
Bash的多选复合命令称为case,语法为:
case word in
[pattern [|pattern]...] commands ;;]...
esac
特定是,case命令检测一个变量值,试图匹配其中一个具体的模式,
当与之相匹配的模式找到之后,就会执行与改模式相关联的命令。
若找到一个模式之后,就不会再继续寻找,模式以一个‘)’为终止符。
脚本例子:
#!/bin/bash
echo "
please select:
1. Display System Information
0. Quit
"
read -p "Enter selection [0-1] >"
case $REPLY in
0) echo "Program terminated."
exit
;;
1) echo "Hostname: $HOSTNAME"
uptime
;;
*) echo "Invalid entry" >&2
exit
;;
esac实验例子:
Enter selection [0-1] >1
Hostname: localhost.localdomain
10:42:19 up 2 days, 6:41, 4 users, load average: 0.00, 0.06, 0.06
[seven@localhost playground]$ bash 32.1.TXTplease select:
1. Display System Information
0. QuitEnter selection [0-1] >0
Program terminated.
[seven@localhost playground]$ bash 32.1.TXTplease select:
1. Display System Information
0. QuitEnter selection [0-1] >3
Invalid entry
32.2 模式
上面的case例子中,模式以一个‘)’为终止符,除了常见的外,还有冷门的。
如:[[:alpha:]] 描述 若单词是一个字母字符
???) 描述 若单词只有3个字符
*.txt 描述 若单词以 ‘.txt’ 字符结尾
*) 描述 匹配任意单词,相当于末尾的else
脚本例子:
#!/bin/bash
read -p "enter word > "
case $REPLY in
[[:alpha:]]) echo "is a single alphabetic character." ;;
[ABC][0-9]) echo "is A, B, or C followed by a digit." ;;
???) echo "is three characters long." ;;
*.txt) echo "is a word ending in '.txt'" ;;
*) echo "is something else." ;;
esac
实验例子:
[seven@localhost playground]$ bash 32.1.TXT
please select:
1. Display System Information
0. QuitEnter selection [0-1] >3
Invalid entry
[seven@localhost playground]$ bash 32.2.txt
enter word > 666.txt
is a word ending in '.txt'32.3 执行多个动作
这是一个新特性,在bash4.0以前,case只能匹配一个模式,然后终止。
bash4.0以后 末尾使用 ;;& 可以让代码继续传递下去,
类似于 c语言的switch用法第三十三期33-位置参数
位置参数是 shell特有的东西,首先它是一个参数集合,
集合中包含了命令行中所有独立的单词。
学习它的意义在于,在此之前的程序还缺少接受和处理参数的能力。
本章将学习让程序访问命令行内容。33.1 访问命令行
位置参数的变量集合中的变量按照从0-9给予命名。也可以大于9,例如: ${10}
[seven@localhost playground]$ bash 33.1.a.txt
$0 = 33.1.a.txt
$1 =
$2 =1.确定参数个数: $# 这个变量代表参数个数
参数会从$1开始算
[seven@localhost playground]$ bash 33.txt abq.iop
Number of arguments: 1
$0 = 33.txt
$1 = abq.iop
$2 =
2.shift-遍历神器:每次执行shift命令,变量$2移动到$1,以此类推。
与此同时变量$#的值也会相应减1,注意$0的值永不变。
3.简单应用:这里不使用shift命令,我们仅仅是输出,当前文件信息。
这里 PROGNAME 就是 basename $0 命令的执行结果,清除路径开头。
故 $1 = PROGNAME = $(basename $0)
#!/bin/bash
# file_info: simple file information program
PROGNAME=$(basename $0)
if [[ -e $1 ]]; then
echo -e "\nFile Type:"
file $1
echo -e "\nFile Status:"
stat $1
else
echo "$PROGNAME:usage:$PROGNAME file" >&2
exit 1
fi4.Shell 函数中使用位置参数
FUNCNAME变量将由shell自动更新,以便跟踪当前执行的shell函数。
调用下面这个函数,注意是脚本调用函数,需要带上一个文件名参数。
file_info () {
# file_info: function to display file information
if [[ -e $1 ]]; then
echo -e "\nFile Type:"
file $1
else
echo "$FUNCNAME: usage: $FUNCNAME file" >&2
return 1
fi
}33.2 处理集体位置参数
shell提供了四种不同的得到位置参数列表的方法,但是目前为止,("$@") 是最好用的。
因为它保留了每一个位置参数的完整性。33.3 一个更复杂的应用
我们将加强前面的 sys_info_page , 添加 输出文件(-f file)、交互模式(-i)、帮助(-h)
usage () {
echo "$PROGNAME: usage: $PROGNAME [-f file | -i]"
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这里省略一个100行的程序,看不懂。
第三十四章34-流程控制(for循环)
本章是流程控制的最后一章。本章学习for循环。
现代版的bash,有两种for循环的格式,任君选择。34.1 for:传统shell格式
for variable [in words]; do
commands
done
可以使用{A..Z} 创建words列表
实验:[seven@localhost playground]$ for i in A B C;do echo $i; done
A
B
C34.2 C语言格式
for (( expression1 ))
while (( expression2 ));do
commands
(( expression3 ))
done实验:[seven@localhost playground]$ for ((i=0 ;i<2;i++));
> do echo $i
> done
0
1
34.3 总结
report_home_space 函数加大难度,在练习中复习命令。report_home_space () {
if [[ $(id -u) -eq 0 ]]; then # admin
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
}
继续加大难度
report_home_space () {
local format="%8s%10s%10s\n"
local i dir_list total_files total_dirs total_size user_name
if [[ $(id -u) -eq 0 ]]; then # admin
dir_list=/home/*
user_name="All Users"
else
dir_list=$HOME
user_name=$USER
fi
echo "<H2>Home Space Utilization ($user_name)</H2>"
for i in $dir_list; do
total_files=$(find $i -type f | wc -l)
total_dirs=$(find $i -type d | wc -l)
total_size=$(du -sh $i | cut -f 1)
echo "<H3>$i</H3>"
echo "<PRE>"
printf "$format" "Dirs" "Files" "Size"
printf "$format" "----" "-----" "----"
printf "$format" $total_dirs $total_files $total_size
echo "</PRE>"
done
return
}
以上内容用到了,目前为止我们学过的许多知识。第三十五章35-字符串和数字
之前学了文件的读写操作,本章学更细致的,比如说处理字符串和数字。
再后面的学习中,使用bc,完成高级的数学运算。35.1 参数展开
大多数的参数展开会出现在脚本中,我们也曾经使用过一些形式的参数展开。
例如,shell变量,echo $HOME
1.基本参数:最基本的参数展开形式就是${a},这样就变成a所包含的值。
稍微复杂点的形式是 echo "${a}_file",变成字符串拼接。
2.管理空变量的展开
如果对一个变量展开后发现是空变量,这就很恶心了,
我们希望有一个默认值,而不是光秃秃的的空变量。
${parameter:-word}, 默认值填在word处。
当parameter展开为空时,默认值就会起作用。
如果我们要求很严格,发现空变量就要报告发生错误退出,
${parameter:?"some word to send!!!"}
当parameter展开为空时,word的内容就会发送到标准错误。
如果我们想搞事情,发现空变量不管,如果不为空就替换。
${parameter:+"word you want substitute!!"}
当parameter展开不为空时,发生变量替换。35.2 返回变量名的参数展开
shell具有返回变量名的能力,这个功能会用在相当独特的环境。
${!prefix*}
${!prefix@}
这种展开会返回以prefix开头的已有变量名。1.字符串展开
${#parameter}
经典案例,路径名的展开。
[seven@localhost ~]$ foo="This string is long."
[seven@localhost ~]$ echo "'$foo' is ${#foo} characters long."
'This string is long.' is 20 characters long.
${parameter:offset:length}
子串的提取,始于第offset字符,长度为length
[seven@localhost ~]$ echo ${foo:5}
string is long.
[seven@localhost ~]$ echo ${foo:5:6}
string
如果offset是负数,表示字符串倒数第n个位置开始。
${parameter#pattern}
${parameter##pattern}
这些展开会从paramter中删除一部分,通过匹配的文本。
# 表示清除最短的匹配结果,##表示清除最长的匹配结果
[seven@localhost ~]$ foo=file.txt.zip
[seven@localhost ~]$ echo ${foo#*.}
txt.zip
[seven@localhost ~]$ echo ${foo##*.}
zip
${parameter%pattern}
${parameter%%pattern}
与#和##类型,区别是它们清除的文本从末尾开始,而不是开头
[seven@localhost ~]$ foo=file.txt.zip
[seven@localhost ~]$ echo ${foo%.*}
file.txt
[seven@localhost ~]$ echo ${foo%%.*}
file
${parameter/pattern/string} 只替换第一个
${parameter//pattern/string} 替换全部
${parameter/#pattern/string} 匹配项出现在串的开头
${parameter/%pattern/string} 匹配项出现在串的末尾这个脚本的功能是 查找最长的字符串长度
难点梳理:
这里出现了简写,用参数展开 ${#j} 取代命令 $(echo $j | wc -c)
strings 表示 string数组
看不懂最外面那层的for,干哈用。
#!/bin/bash
# longest-word3 : find longest string in a file
for i; do
if [[ -r $i ]]; then
max_word=
max_len=
for j in $(strings $i); do
len=${#j}
if (( len > max_len )); then
max_len=$len
max_word=$j
fi
done
echo "$i: '$max_word' ($max_len characters)"
fi
shift
done2.大小写转换
这个功能除了提高审美价值,还有就是规范化用户输入。
[[$1]] 是一个字符串变量
echo ${1,,} 表示全部字母转成小写
echo ${1,} 表示仅仅把第一个字符转成小写
echo ${1^^} 表示全部字母转成大写字母
echo ${1^} 表示仅仅把第一个字符转成大写字母#!/bin/bash
# ul-param - demonstrate case conversion via parameter expansion
if [[ $1 ]]; then
echo ${1,,}
echo ${1,}
echo ${1^^}
echo ${1^}
fi
实验例子:
[seven@localhost playground]$ bash 35.2.txt aBc
abc
aBc
ABC
ABc
35.3 算术求值和展开
前面的第七章学过算术展开,$((expression)),但是它对小数无能为力。1.复习一下,复合命令(()),此命令用做算术求值(真测试)
2.复习一下,第九章中我们发现shell支持任意进制的任意整形常量。
number :默认为10进制数,以10为底
0number:被认为是八进制数
0xnumber:十六进制表示法
base#number:以base为底的数
实验例子:
[seven@localhost playground]$ echo $((0xff))
255
[seven@localhost playground]$ echo $((2#11111111))
2553.位运算,~(按位取反),《(左移变大),》(右移变小)
4.逻辑运算符
逻辑运算符有好多,类似于小于,大于,不小于,就不多说了。
特别提一下,三目运算符中的 +=或-=得加上括号,否则报错。
35.4 bc(一种高精度计时器语言)
整形算术很强大,但是如果想用浮点数,就歇菜了,至少shell歇菜了,
shell脚本得求助使用bc脚本。
通过 bc < xx符号可以把xx命令的标准输出给 bc
通过 bc <<< "2+2" 可以使用here字符串传递脚本给bc1. 使用bc的案例:
[seven@localhost playground]$ bc 35.4.1.txt
bc 1.06.95
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type warranty.
4
quit
[seven@localhost playground]$ bc -q
2 + 2
4
quit
[seven@localhost playground]$ bc < 35.4.1.txt
4
[seven@localhost playground]$ bc <<< "2+2"
42. 计算每月的还贷金额
#!/bin/bash
# loan-calc : script to calculate monthly loan payments
PROGNAME=$(basename $0)
usage () {
cat <<- EOF
Usage: $PROGNAME PRINCIPAL INTEREST MONTHS
Where:
PRINCIPAL is the amount of the loan.
INTEREST is the APR as a number (7% = 0.07).
MONTHS is the length of the loan's term.
EOF
}
if (($# != 3)); then
usage
exit 1
fi
principal=$1
interest=$2
months=$3
bc <<- EOF
scale = 10
i = $interest / 12
p = $principal
n = $months
a = p * ((i * ((1 + i) ^ n)) / (((1 + i) ^ n) - 1))
print a, "\n"
EOF
实验例子:
[seven@localhost playground]$ 35.4.2.txt 135000 0.0775 180
bash: 35.4.2.txt: command not found
[seven@localhost playground]$ bash 35.4.2.txt 135000 0.0775 180
1270.7222490000第三十六章36-数组
数组的特性是:允许存放多个值在一个变量中。shell支持数组,
学习数组,让我们的shell命令更强大。36.1 什么是数组
数组是一次能存放多个数据的变量,数组里面有单元格,一个单元格存一个元素,
bash中的数组仅限制为单一维度,尽管有限制,它依然很流行。
shell版本2开始支持数组,这意味着,原来的unix shell程序,sh根本就不支持数组。36.2 创建一个数组
和其他脚本语言一样,变量会在第一次访问时自动创建,也可以事先用declare声明
[seven@localhost playground]$ abc[1]=foot
[seven@localhost playground]$ echo ${abc[1]}
foot
[seven@localhost playground]$ declare -a abc
[seven@localhost playground]$ echo ${abc[1]}
foot36.3 数组赋值
给一个数组各个元素,批量赋值使用小括号,不可使用大括号
foo[223]=value
foo=(3 4 5 6)
days=(Sun Mon Tue Wed Thu Fri Sat)
days=([0]=Sun [1]=Mon [2]=Tue)
36.4 访问数组元素
hours命令,被用来确定什么时段一个系统最活跃,对当前目录中的文件修改次数进行统计。hours脚本如下:
#!/bin/bash
# hours : script to count files by modification time
usage (){
echo "usage: $(basename $0) directory" >&2
}
# Check that argument is a directory
if [[ ! -d $1 ]]; then
usage
exit 1
fi
# Initialize array
for i in {0-23}; do hours[i]=0; done
# Collect data
for i in $(stat -c %y "$1"/* | cut -c 12-13); do
j=$(i/#0)
((++hours[j]))
((++count))
done
# Display data
echo -e "Hour\tFiles\tHour\tFiles"
echo -e ----\t----\t----\t----""
for i in {0..11}; do
j=$((i + 12))
printf "%02d\t%d\t%02d\t%d\n" $i ${hours[i]} $j ${hours[j]}
done
printf "\nTotal files = %d\n" $count实验例子:
[seven@localhost playground]$ bash 36.4.txt .
。。。。。。。。。。。。。。。。。
Hour Files Hour Files
----t----t----t----
00 32 12 0
01 13 00 0
02 14 00 0
03 15 00 0
04 16 00 0
05 17 00 0
06 18 00 0
07 19 00 0
08 20 00 0
09 21 00 0
10 22 00 0
11 23 00 0Total files = 32
36.5 数组操作
学习删除数组,确定数组大小,排序等操作。
*和@可以被用来访问数组中的每个元素。
表示法${animals[*]}和{animals[@]}的行为是一致的除非被引号引起来
animals=("a dog" "a cat" "a fish")实验例子:
[seven@localhost playground]$ for i in ${animals[@]}; do echo $i; done
a
dog
a
cat
a
fish
[seven@localhost playground]$ for i in "${animals[*]}"; do echo $i; done
a dog a cat a fish
[seven@localhost playground]$ for i in "${animals[@]}"; do echo $i; done
a dog
a cat
a fish
统计数组元素个数,特别注意shell中的数组空白元素不统计
统计数组个数的用法 ${#array[@]}
a[100]=foot
echo ${#a[@]}
echo ${#a[100]}
实验例子:
[seven@localhost playground]$ a[100]=foot
[seven@localhost playground]$ echo ${#a[@]}
1
[seven@localhost playground]$ echo ${#a[100]}
4
数组使用的下标:前面说,shell中不统计空白元素的个数,我们该如何找到?
正因为shell允许赋值的数组元素间存在间隔,有时候确定那个元素存在得花点脑经。
关键点:*或者@ 能展开成分离的词,加上感叹号不输出内容只输出下标。
${!array[*]}
${!array[@]}for=([2]=a [4]=b [6]=c)
for i in "${foo[@]}"; do echo $i; done
for i in "${!foo[@]}"; do echo $i; done
实验例子:
[seven@localhost playground]$ for i in "${foo[@]}"; do echo $i; done
a
b
c
[seven@localhost playground]$ for i in "${!foo[@]}"; do echo $i; done
2
4
6
数组末尾添加元素
想在末尾添加元素,知道数组中的个数是没用的,因为shell中的数组空白元素不统计,
通过*和@表示法,返回的数值也不能告诉我们使用的最大数组索引。我们需要学习新的用法。
foo=(a b c)
echo ${foo[@]}
foo+=(d e f)
echo ${foo[@]}
实验例子:
[seven@localhost playground]$ a[100]=foot;
[seven@localhost playground]$ a+=(4 5 6)
[seven@localhost playground]$ echo ${!a[@]}
100 101 102 103
[seven@localhost playground]$ echo ${a[@]}
foot 4 5 6数组排序,shell中没有数组排序的方法,需要我们自己去实现
脚本如下:
#!/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)) # 为什么要在for前使用$,这里的$是给sort使用的。
echo "Sorted array: ${a_sorted[@]}"实验例子:
[seven@localhost playground]$ bash array-sort
Original array: f e d c b a
Sorted array: a b c d e f删除数组:
foo=(a b c d e f)
echo ${foo[@]}unset foo
echo ${foo[@]}foo=(a b c d e f)
echo ${foo[@]}unset 'foo[2]'
echo ${foo[@]}foo=(a b c d e f)
foo=
echo ${foo[@]}
这个例子说明,任何引用一个不带下标的数组变量,则指的是数组元素0
实验例子:
[seven@localhost playground]$ echo ${foo[@]}
a b c d e f
[seven@localhost playground]$ unset foo
[seven@localhost playground]$ echo ${foo[@]}[seven@localhost playground]$ foo=(a b c d e f)
[seven@localhost playground]$ foo=
[seven@localhost playground]$ echo ${foo[@]}
b c d e f
36.6 关联数组
最新版本的特性,
关联数组使用字符串作为数组索引。
declare -A colors
colors["red"]="#ff0000"
colors["green"]="#00ff00"
colors["blue"]="#0000ff"echo ${colors["blue"]}
书中说,关联数组必须使用带 -A 选项的declare命令创建,
在center os7上实验发现两种方法都可以实现。
实验例子:
[seven@localhost playground]$ color["abc"]="blue"
[seven@localhost playground]$ echo ${color["abc"]}
blue第三十七章37-奇珍异宝
本章是本书最后一站,这将介绍些零星的知识点。
之前的400多页里,linux shell的很多方面都已佘略,
但是还有许多bash特性没有佘略,在本书的最后,
说点虽然不常用的知识点,但是对某些程序可能很有帮助。37.1 组命令和子shell
把命令组合在一起执行,
组命令使用花括号,子shell用括号。
注意语法要求,花括号与命令要有空格,并且最后一个命令必须用一个分号,或者一个换行符。{ ls -l; echo "Listing of foo.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; } | lpr进程替换的问题:
组命令和子shell看上去很相似,都可以重定向中合并流,但是两者之间有一个很重要的不同。
组命令在当前shell中执行它的所有命令,而一个子shell在当前shell的一个子副本中执行它的命令。
所以,大多数情况下,倾向于使用组命令。echo "foo" | read
echo $REPLY因为read命令在子shell中执行,REPLY副本会自动销毁。
如果非得使用子shell,可以使用 <(list) 叫做进程替换技术<(list) 适用于产生标准输出的进程
>(list) 适用于产生标准输入的进程这里的list是一串命令的集合列表,
read < <(echo "foo")
echo $REPLY进程替换允许我们把一个子shell的结果单做一个用于重定向的普通文件。
echo <(echo "foo")
进程替换经常被包含read命令的循环用到。
脚本如下:
#!/bin/bash
# pro-sub : demo of process substitution
while read attr links owner group size date time filename; do
cat <<- EOF
Filename: $filename
Size: $size
Owner: $owner
Group: $group
Modified: $date $time
Links: $links
Attributes: $attr
EOF
done < <(ls -l | tail -n +2)
实验例子:(此脚本报语法错误)
37.2 陷阱
若一个脚本接收到即将提前终止的信号,此时让脚本删除创建的临时文件,这对一个大项目很有用。
为了满足这个需求,bash提供了trap机制,就是众所周知的陷阱。
语法为: trap argument signal[signal...]
argument 是一串字符串,它被读取并单做一个命令,
signal 是一个信号的说明,它会触发执行所要解释的命令。SIGINT 或 SIGTERM 信号
这个脚本运行需要25秒,如果提前用ctrl+c 结束就会触发 echo
脚本如下:
#!/bin/bash
# trap-demo : simple signal handling demo
trap "echo 'I am ignoring you.'" SIGINT SIGTERM
for i in {1..5}; do
echo "Iteration $i of 5"
sleep 5
done构建一个字符串形成一个有用的命令序列是很笨拙的,通常会指定一个shell
函数作为命令,每个型号指定一个单独的shell函数来处理。脚本如下:
#!/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
37.3 异步执行
异步是各干各的,想要保持父子脚本之间协调工作,又想异步,这得使用wait,
同步执行的缺点是,一个脚本必须等待另一个脚本结束任务之后,才能完成自己的任务。
在shell中,使用wait命令会导致父脚本暂停运行,直到一个特定的进程结束运行。
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."
37.4 命名管道
命名管道的行为类似于文件,但实际上形成了FIFO的缓冲,和普通(未命名)管道一样
数据从一端进入,然后从另一端出现。
process1 | named_pipe
和
process2 | named_pipe
表现出来的形式是 process1 | process2实验例子:
第一步,设置一个命名管道: mkfifo pipe1
ls -l pipe1
第二步,我们需要两个shell窗口来演示
在第一个终端中,ls -l > pipe1,此时会阻塞。
在第二个终端中,cat < pipe1,执行完成后,两个命令都已结束。
注意: 两个终端得在一个目录下执行命令例如同在 playground文件夹下。37.5 总结
学完本书,shell命令剩下的唯一要做的事就是练习,再练习。纵然我们一路过来敲了很多,
但只是接触了它的表面,做到熟能生巧,在实际的开发生产中,才会得心应手。