shell脚本学习笔记
1、判断符号:中括号[ ]
[ ]进行数据的判断,例如我想知道HOME这个变量是否为空,[ -z "$HOME" ],或者两个字符串是否相等,[ "$HOME" == "$MALI" ]。由于中括号使用的地方有很多,所以在bash中作为判断式时,中括号的两端需要有空格来分隔。在使用中需要注意:
-
在中括号内的每个组件都需要有空格键来分隔
-
在中括号内的变量,最好以双引号括号起来,例如"$HOME"
-
在中括号内的常量,最好以单或者双引号括起来,
read -p "please input (Y/N)" yn
[ "$yn" == "Y" -o "$yn" == "y" ] && echo "OK continue" && exit 0
[ "$yn" == "N" -o "$yn" == "n" ] && echo "Oh, interrupt" && exit 0
2、shell的默认变量
$0 :执行脚本的完整文件名 $1:第一个参数 $2:第二个参数 …………
$#:代表后接的参数“个数”
$@:参数内容,代表“$1”“$2”“$3”,每个变量是独立的
$*:代表“$1c$2c$3c$4”c为分隔符,一般为空格,
$$:脚本本身的进程PID
shift变量:shift n 会移动变量,去除第n个变量,变量集体左移以为,第一位消失。
测试脚本:
1
2
3
4
5
6
7
8
9
10
|
#!/bin/bash echo "the script name is: ==> $0" echo "total parameter number is : ==> $#" echo "your whole parameter is: ==> $@" echo "your whole parameter222 is: ==> $@" echo "the 1st parameter is: ==> $1" echo "the 2st parameter is: ==> $2" shift 2 echo "After shift total parameter number is : ==> $#" echo "After shift the 1st parameter is: ==> $1" |
结果:
1
2
3
4
5
6
7
8
9
|
./sh07.sh I Love You the script name is: ==> ./sh07.sh total parameter number is : ==> 3 your whole parameter is: ==> I Love You your whole parameter222 is: ==> I Love You the 1st parameter is: ==> I the 2st parameter is: ==> Love After shift total parameter number is : ==> 1 After shift the 1st parameter is: ==> You |
3、条件判断式
if [ 条件判读式一 ];then
条件一成立
elif [ 条件判读式二 ];then
条件二成立
else
条件不成立
fi
if 命令不仅能测试由方括号括起来的条件,也能测试任何命令。if echo "$word" | grep -q "$letter_sequence";then
当if和then在同一行的时候,一个分号(;)必须用在if语句的结尾。if和then都是关键字。关键字(或命令)开始一个语句,如果在同一行开始另一个新语句时,前面一个语句必须用分号(;)结束。
用[[ ... ]]测试结构比用[ ... ]更能防止脚本里的许多逻辑错误。比如说,&&,||,<和>操作符能在一个[[]]测试里通过,但在[]结构会发生错误。在一个if的后面,不必一定是test命令或是test结构([]或是[[...]])
break命令可以带一个参数.一个不带参数的break循环只能退出最内层的循环,而break N可以退出N层循环.
continue命令也可以像break带一个参数.一个不带参数的continue命令只去掉本次循环的剩余代码.而continue N将会把N层循环剩余的代码都去掉,但是循环的次数不变。尽量避免.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
for outer in I II III IV V do # 外部循环do echo; echo -n "Group $outer: " for inner in 1 2 3 4 5 6 7 8 9 10 # 内部循环 do if [ "$inner" -eq 7 ] then continue 2 # continue 2层, 也就是到outer循环上. # 将"continue 2"替换为一个单独的"continue" # 来看一下一个正常循环的行为. fi echo -n "$inner " # 7 8 9 10 将不会被echo done done |
case $变量名 in
"第一个变量的内容")
程序
;;
"第一个变量的内容")
程序
;;
*)
不包含第一个变量内容和第二个
exit 1
;;
esac
不定循环
while [ condition ] #条件成立就循环
do
程序段
done
或者
until [ condition ] #条件成立终止循环
do
程序段
done
固定循环
for var in con1,con2,con3 …
do
程序段
done
适合数值处理for循环
for ((初始值;限制值;步长))
do
程序段
done
4、函数function
function fname(){
函数内容
}
shell script的执行方式是从上到下的,所以一些函数需要在使用之前就给出。函数内有有内置变量的,调用的时候 fname one two,那么函数内的$1为one $2为two
而script的内置变量$1,还是.sh后跟着的变量。
5、shell的追踪和调试
sh -n xx.sh:不要执行,仅查询语法错误
sh -v xx.sh:在执行script之前先将内容输出到屏幕
sh -x xx.sh:将使用到的script内容显示到屏幕上
6、重定向
> ; &> ; >& ; >>
scriptname >filename 重定向scriptname的输出到文件filename中去.,如果文件filename存在则将会被覆盖。
command &> filename 会重定向命令command标准输出(stdout)和标准错误(stderr)到文件filename中。
command >& 2 把命令command的标准输出(stdout)重定向到标准错误(stderr)。
scriptname >>filename 把脚本scriptname的输出追加到文件filename.如果filename不存在,则它会被创建.
7、变量
variable是变量名,$variable就是引用这个变量的值,也是${variable}的一种简写形式。变量赋值的时候,=号两边都不能有空格。如果有一个空白符会怎么样? 如果用 "VARIABLE =value", 脚本会以为"VARIABLE"是一个命令并且此命令带了一个参数"=value"。 如果用 "VARIABLE= value", 脚本会以为"value"是一个命令, 并且把环境变量"VARIABLE"赋为空值:""。一个未初始化的变量有一个”null”值―表示从没有被赋值过(注意null值不等于零)。在一个变量从未赋值之前就使用它通常会引起问题。unset variable 销毁变量。
变量赋值,可以使用= 、也可以使用单引号variable=`ls -l`,这个是将ls的结果赋给变量。也可以使用$(……),也是将括号内的结果赋给变量,variable=$(ls -l)。
b=${a/23/BB}:将a中的"23"替换为"BB"并赋给变量b 反斜杠表示替换。
在Bash中的变量是无类型的,允许变量有整数计算和比较。其中的决定因素是变量的值是不是只含有数字。
环境变量,使用export导出,只能导出到子进程中,到由此脚本生成的命令或进程中。
${parameter-default}, ${parameter:-default}:如果变量没有被设置,使用默认值。
${parameter+alt_value}, ${parameter:+alt_value}:如果变量parameter设置,使用alt_value作为新值,否则使用空字符串。
${parameter?err_msg}, ${parameter:?err_msg}:如果变量parameter已经设置,则使用该值,否则打印err_msg错误信息。
8、退出
exit命令一般用于结束一个脚本,就像C语言的exit一样。它也能返回一个值给父进程。一个命令执行成功返回0,一个执行不成功的命令则返回一个非零值,此值通常可以被解释成一个对应的错误值。
如果一个脚本以不带参数的exit命令结束,脚本的退出状态码将会是执行exit命令前的最后一个命令的退出码。脚本结束没有exit,不带参数的exit和exit $?三者是等价的,都是以最后命令的退出码退出脚本。
$?变量保存了最后一个命令执行后的退出状态。当一个函数返回时,$?保存了函数里最后一个命令的退出状态码。这就是Bash里函数返回值的处理办法。当一个脚本运行结束,$? 变量保存脚本的退出状态,而脚本的退出状态则是脚本中最后一个已执行命令的退出状态。并且依照惯例,0表示执行成功,1-255的整数范围表示错误。$? 变量用于测试脚本中的命令执行结果非常的有用。
逻辑非操作符!:反转一个命令或一个测试的结果,它也能反转退出状态。!后面需要跟一个空格,! true ==false,如果没有跟空格,则表示重复执行上次的命令。
9、使用(( ... ))进行算术
(( ))结构扩展并计算一个算术表达式的值。如果表达式值为0,会返回1或假作为退出状态码。一个非零值的表达式返回一个0或真作为退出状态码。它和先前test命令及[]结构的讨论刚好相反。
((0)) $?值为1;((1)) $?值为0 ((表达式)):表达式的值为0,$?值为1,表达式的值为1,$?值为0。
(( 5>9 )) $?值为1;(( 5<9 )) $?值为0 (( 表达式 )):表达式的值为false,$?值为1,表达式的值为true,$?值为0。
shell程序中的操作默认都是字符串操作。
let :可以使用let来指示下面是算术表达式,let表达式内变量不用加$,必须还完整的算术表达式,即有等号两边。这个可以等同于(( ))结构。let var=var+1 等同于 let "var = var + 1" 、let “var += 1”
expr:通用求值表达式,通过给定的操作(参数必须以空格分开)连接参数,并对参数求值.可以使算术操作, 比较操作, 字符串操作或者是逻辑操作。expr 5 - 3、expr 5 \* 3、y=`expr $y + 1` <==> let y=y+1
expr也可以用于字符串的操作:z=`expr substr $string $position $length`在位置$position上提取$length长度的子串。
b=`expr length $a`:长度: 字符串长度
b=`expr substr $a 2 6` # substr: 从指定位置提取指定长度的字串.
b=`expr match "$a" '[0-9]*'`:从字符串的开始进行搜索,并匹配第一个匹配的字符串,使用正则表达式。 : 操作可以替换match. 比如, b=`expr $a : [0-9]*`与上边所使用的 b=`expr match $a [0-9]*` 完全等价.
10、test 命令
-e:文件是否存在
-f:文件是一个普通文件(不是一个目录或是一个设备文件)
-s:文件大小不为零
-d:文件是一个目录
-b:文件是一个块设备(软盘, 光驱, 等等.)
-h:文件是一个符号链接
-p:文件是一个管道
-L:文件是一个符号链接
-z:字符串为"null",即是指字符串长度为零。
-n:字符串不为"null",即长度不为零.
-r:文件是否可读 (指运行这个测试命令的用户的读权限)
-w:文件是否可写 (指运行这个测试命令的用户的读权限)
-x:文件是否可执行 (指运行这个测试命令的用户的读权限)
f1 -nt f2:文件f1比f2新
f1 -ot f2:文件f1比f2旧
f1 -ef f2:文件f1和f2 是相同文件的硬链接
!:"非" -- 反转上面所有测试的结果(如果没有给出条件则返回真).
-eq:等于 if [ "$a" -eq "$b" ]
-ne:不等于 if [ "$a" -ne "$b" ]
-gt:大于 if [ "$a" -gt "$b" ]
-ge:大于等于 if [ "$a" -ge "$b" ]
-lt:小于 if [ "$a" -lt "$b" ]
-le:小于等于 if [ "$a" -le "$b" ]
<:小于(在双括号里使用)(("$a" < "$b")) 。if [[ "$a" < "$b" ]] if [ "$a" \< "$b" ] 注意"<"字符在[ ] 结构里需要转义
<=:小于等于 (在双括号里使用)(("$a" <= "$b"))
>:大于 (在双括号里使用)(("$a" > "$b"))
>=:大于等于(在双括号里使用)(("$a" >= "$b"))
11、字符串比较
=:等于 if [ "$a" = "$b" ] ==:等于 if [ "$a" == "$b" ] 它和=是同义词。==比较操作符在一个双方括号测试和一个单方括号号里意思不同。[[ $a == z* ]] # 如果变量$a以字符"z"开始(模式匹配)则为真。[[ $a == "z*" ]] # 如果变量$a与z*(字面上的匹配)相等则为真。
字符串长度:${#string}、expr length $string、expr "$string" : '.*'
例外情况:${#*}和${#@} 表示位置参数的个数。对于一个数组来说,${#array[*]}和${#array[@]}表示数组中元素的个数.
索引:expr index $string $substring ##在字符串$string中$substring第一次出现的数字位置
子串提取:${string:position} #把$string中从第$postion个字符开始字符串提取出来,如果$string是"*"或"@",则表示从位置参数中提取第$postion后面的字符串。
${string:position:length} #把$string中$postion个字符后面的长度为$length的字符串提取出来。
子串替换:${string/substring/replacement} 用$replacement替换由$substring匹配的第一个字符串。
${string//substring/replacement} 用$replacement替换所有匹配$substring的字符串。
${string/#substring/replacement} 如果$string字符串的最前端匹配$substring字符串,用$replacement替换$substring.
${string/%substring/replacement} 如果$string字符串的最后端匹配$substring字符串,用$replacement替换$substring.
1
2
3
4
5
6
7
8
9
10
|
str=abcABC123ABCabc echo ${str/abc/xyz} echo ${str//abc/xyz} echo ${str/ #abc/xyz} echo ${str/%abc/xyz} 输出: xyzABC123ABCabc xyzABC123ABCxyz xyzABC123ABCabc abcABC123ABCxyz |
${var#Pattern}, ${var##Pattern}:删除从$var前端开始的最短或最长匹配$Pattern的字符串。
${var%Pattern}, ${var%%Pattern}:删除从$var后端开始的最短或最长匹配$Pattern的字符串。
${!varprefix*}, ${!varprefix@} :匹配所有前面声明过的变量,并且变量名以varprefix开头。
1
2
3
4
5
6
7
|
xyz23=whatever xyz24= a=${!xyz*} # 展开为声明过的并以"xyz". echo "a = $a" # a = xyz23 xyz24 a=${!xyz@} # Same as above. echo "a = $a" # a = xyz23 xyz24 |
12、declare
declare或typeset内建命令(它们是完全相同的)可以用来限定变量的属性。这是在某些编程语言中使用的定义类型不严格的方式。
declare -i var :将var限定成正数。eclare命令允许在声明变量类型的时候同时给变量赋值,declare -i var=88
-r :只读
-i: 整数
-a: 数组
-f :函数 在脚本中没有带任何参数的declare -f 会列出所有在此脚本前面已定义的函数出来。
13、shell命令和内部命令结合
cat :把文件的内容输出到stdout. 当与重定向操作符 (> 或 >>)结合使用时, 一般都是用来将多个文件连接起来。cat file1,file2, file3 > file。-s选项可以把多个空行压缩成一个空行;-n 选项是为了在目标文件中的所有行前边插入行号;-b 选项 与 -n 选项一样, 区别是不对空行进行编号。
rev:把每一行中的内容反转, 并且输出到 stdout上。
1
2
3
4
5
6
7
8
9
10
11
|
bash$ cat file1.txt This is line 1. This is line 2. bash$ tac file1.txt This is line 2. This is line 1. bash$ rev file1.txt .1 enil si sihT .2 enil si sihT |
cp:cp file1 file2 把 file1 拷贝到 file2, 如果存在 file2 的话,那 file2 将被覆盖。 -a 归档 选项 (为了copy一个完整的目录树), -u 是更新选项, 和 -r 与 -R 递归选项.
rm:删除(清除)一个或多个文件. -f 选项将强制删除文件,即使这个文件是只读的.并且可以用来避免用户输入(在非交互脚本中使用)。当使用递归参数 -r时, rm 命令将会删除整个目录树. 如果不慎使用 rm -rf *那整个目录树就真的完了。
1
2
3
4
5
|
cat list-1 list-2 list-3 | sort | uniq > final.list # 将3个文件连接起来, # 将它们排序, # 删除其中重复的行, # 最后将结果重定向到一个文件中. |
sort INPUTFILE | uniq -c | sort -nr 命令 先对 INPUTFILE 排序, 然后统计 每行出现的次数, 最后的(-nr 选项将会产生一个数字的反转排序). 这种命令模版一般都用来分析 log 文件或者用来分析字典列表, 或者用在那些需要检查文本词汇结构的地方。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
$ cat testfile This line occurs only once. This line occurs twice. This line occurs twice. This line occurs three times. This line occurs three times. This line occurs three times $ uniq -c testfile 1 This line occurs only once. 2 This line occurs twice. 3 This line occurs three times. $ sort testfile | uniq -c | sort -nr 3 This line occurs three times. 2 This line occurs twice. 1 This line occurs only once.
|