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.

 

原文地址:https://www.cnblogs.com/silence-hust/p/4534616.html