[ubuntu入门手册]-16-Shell脚本基础
引言
这篇文件介绍linux中shell脚本语法,linux的shell脚本逻辑远没有编程语言难,有过编程语言学习的朋友可以很容易的上手,linux下的shell就像windows下的batch脚本,可以用于集成系统终端任何命令,让我们的操作更加的快捷。
文章目录
- 0×1.变量的定义与输出
- 0×2.键盘输入与参数传递
- 0×3.dash语法错误的解决方法
- 0×4.if..else..fi实例
- 0×5.case实例
- 0×6.for循环实例
- 0×7.while循环实例
- 0×8.until循环实例
- 0×9.shell脚本调试
0×1.变量的定义与输出
shell脚本实际上就是将我们在终端中的命令集成起来批量的执行,用规定的语法结构来控制脚本的执行流程,系统将我们编写的sh脚本交给"解释器"执行,最常见的解释器就是bash,用下面的命令可以查看系统中所有可用的shell解释器:
qing@qingsword.com:~$ cat /etc/shells # /etc/shells: valid login shells /bin/sh /bin/dash /bin/bash /bin/rbash /usr/bin/tmux #显示当前系统默认使用的shell解释器 qing@qingsword.com:~$ echo $SHELL /bin/bash
按照国际惯例,第一个shell脚本输出"hello world":
#1.创建并编辑一个shell qing@qingsword.com:~$ vim sh01.sh #2.输入下面的内容 #shell脚本标准第一行注释,代表这是一个bash脚本 #定义字符串变量,在没有使用双引号的时候,输出特殊字符一定要使用转义反斜杠,包括空格。 #!/bin/bash #by www.qingsword.com hw=Hello\!\ world\! echo $hw #输出变量值 #3.执行这个shell(使用bash sh01.sh也可以) qing@qingsword.com:~$ sh sh01.sh Hello! world! #输出 #除了直接使用bash调用脚本外,还可以给脚本添加可执行权限,之后只需要输入脚本命令,系统会使用默认的脚本解释器去执行它,例如 qing@qingsword.com:~$ sudo chmod +x sh01.sh qing@qingsword.com:~$ ./sh01.sh
单引号和双引号的区别:
qing@qingsword.com:~$ vim sh02.sh #!/bin/bash #by www.qingsword.com name="qingsword" #字符串变量 hw="hello!world." myname0="My name is $name" myname1='My name is $name' echo $hw #输出 echo $myname0 echo $myname1 #单引号中的所有字符都将被解释成普通字符 qing@qingsword.com:~$ sh sh02.sh hello!world. My name is qingsword My name is $name
除了上面两种引号外,还有一种特殊的引号,esc下面,数字键1左边的那个键,能够打出一个`,包含在`中的内容,将被解释器直接执行,例如:
#定义一个变量x,这个变量等于一句命令,这句命令被包含在`对中,脚本执行到这里时,将会直接执行语句`tail -n 2 /etc/passwd`并将这条命令执行后的返回结果保存到x变量中,使用echo就能打印出这个变量的值 qing@qingsword.com:~$ x=`tail -n 2 /etc/passwd` qing@qingsword.com:~$ echo $x #可能在有些脚本中,会看到下面这种语法,实际上执行一下就会发现,输出与上面这种完全相同,所以`tail -n 2 /etc/passwd`就等同于$(tail -n 2 /etc/passwd),只是写法不相同 qing@qingsword.com:~$ x=$(tail -n 2 /etc/passwd) qing@qingsword.com:~$ echo $x
0×2.键盘输入与参数传递
使用read关键字,可以读取键盘输入,这有点类似C语言的scanf,请看下面的实例:
qing@qingsword.com:~$ vim sh03.sh #!/bin/bash #by www.qingsword.com echo "Please keyin your name then press Enter:" read name #读取键盘输入保存到name变量中 #上面这两句可以用下面这一句代替,效果是相同的,read的-p参数用于打印一段需要显示在屏幕的文字,随后的name变量接收用户从键盘输入的值 # read -p "Please keyin your name then press Enter:" name # -e参数告诉解释器,启用反斜杠的转义,那么\n就会被解释成一个换行符,如果不想echo命令输出的最后执行换行,可以再添加参数-n(每个echo语句的末尾都会自动换行) echo -e "Hello $name\nWelcome!" #输出 qing@qingsword.com:~$ sh sh03.sh Please keyin your name then press Enter: qingsword Hello qingsword Welcome! #因为echo中添加了换行符'\n',所以Welcome!换行了
可以在脚本后面添加变量参数传递给脚本:
#$0代表被执行的脚本本身,$1,$2,$3表示执行时传递给脚本的参数,可以自定义任意数量的参数传递给脚本 qing@qingsword.com:~$ vim sh04.sh #!/bin/bash #by www.qingsword.com echo "This script's name is:$0" echo "parameters:$1,$2,$3" qing@qingsword.com:~$ sh sh04.sh p1 p2 p3 This script's name is:sh04.sh parameters:p1,p2,p3
0×3.dash语法错误的解决方法
当我们的shell脚本里面存在declare关键字定义的变量时,在终端下使用sh运行脚本会出现“dash语法错误”,因为ubuntu的/bin/sh连接到了dash这个文件(ll /bin/sh就知道了),并不是调用bash执行,dash的declare语法稍有不同,所以我们直接使用sh运行这个shell会出现错误,请看下面的实例:
#declare命令可以直接在终端下定义临时变量并做运算,-i定义整数变量 qing@qingsword.com:~$ declare -i a=1 qing@qingsword.com:~$ declare -i b=2 qing@qingsword.com:~$ declare -i c=$a+$b qing@qingsword.com:~$ echo $c 3 #declare定义变量类型参数 -i 整数 -a array数组 -f 函数 -r 只读 -x 通过环境输出变量 #创建一个shell脚本,计算一个简单加法 qing@qingsword.com:~$ vim declare-demo.sh #!/bin/bash #use bash $0 #by www.qingsword.com declare -i result=1+2+3+4+3+2+1 echo "Result is:$result" #使用sh运行脚本,报错 qing@qingsword.com:~$ sh declare-demo.sh declare-demo.sh: 4: declare-demo.sh: declare: not found #使用bash调用这个sh,正常执行 qing@qingsword.com:~$ bash declare-demo.sh Result is:16
#dash语法错误的解决方法
三种解决方法:
1)执行时由sh build.sh变成bash build.sh。
2)更改软连接,让sh连接到bash,在终端下输入sudo ln -s /bin/bash /bin/sh -f
3)sudo dpkg-reconfigure dash 进行配置,去掉sh软连接关联,道理同(2)
0×4.if..else..fi实例
if是shell中最常用的判断语法,唯一要注意的就是,Linux脚本中if参数与符号之间必须用空格隔开,请看下面的实例:
#读取键盘输入的if脚本实例 qing@qingsword.com:~$ vim ifshell.sh #!/bin/bash #if demo #by www.qingsword.com echo "Please keyin 'y' to continue.." read yn #不同块元素之间必须用空格隔开,否则会报错 if [ "$yn" = "y" ] || [ "$yn" = "Y" ] ; then echo "script is running..." else echo "STOP!" fi #if结束 #执行测试 qing@qingsword.com:~/test$ sh ifshell.sh Please y to continue.. y script is running... qing@qingsword.com:~/test$ sh ifshell.sh Please y to continue.. Y script is running... qing@qingsword.com:~/test$ sh ifshell.sh Please y to continue.. s STOP!
携带初始参数的if脚本实例:
#$1将读取脚本后面传递的第一个参数进行判断 qing@qingsword.com:~$ vim ifshell.sh #!/bin/bash #by www.qingsword.com if [ "$1" = "hello" ] || [ "$1" = "HELLO" ] ; then echo "$1,How are you?" elif [ "$1" = "" ] ; then #如果参数为空 echo "STOP!You must input parameters." else echo "The only accept parameter is ‘hello’." fi #测试 qing@qingsword.com:~$ sh ifshell.sh STOP!You must input parameters. qing@qingsword.com:~$ sh ifshell.sh sdfaf The only accept parameter is hello. qing@qingsword.com:~$ sh ifshell.sh HELLO HELLO,How are you?
一个判断系统是否开启某些端口的shell,可以将命令直接定义成变量,但是命令必须使用键盘1左边那个“点”号扩起来,请看下面的实例:
qing@qingsword.com:~$ vim port.sh #!/bin/bash # 检查www,ftp,ssh以及smtp+pop3是否开启 # by www.qingsword.com # www 切记是1左边那个`不是单引号' www=`netstat -an|grep LISTEN|grep :80` # 检测是否开放ftp21端口 ftp=`netstat -antu|grep LISTEN|grep :21` # 检测是否开放ssh22端口 ssh=`netstat -antu|grep LISTEN|grep :22` # smtp+pop3 smtp=`netstat -antu|grep LISTEN|grep :25` pop3=`netstat -antu|grep LISTEN|grep :110` #变量值是否为空,空表示没有开放此端口 if [ "$www" != "" ] ; then echo "WWW is running" else echo "WWW is NOT running" fi if [ "$ftp" != "" ] ; then echo "FTP is running" else echo "FTP is NOT running" fi if [ "$ssh" != "" ] ; then echo "SSH is running" else echo "SSH is NOT running" fi if [ "$smtp" != "" ] && [ "$pop3" != "" ] ; then echo "SendMail is running" elif [ "$smtp" = "" ] && [ "$pop3" != "" ] ; then echo "SendMail have some problem of your smtp" elif [ "$smtp" != "" ] && [ "$pop3" = "" ] ; then echo "SendMail have some problem of your pop3" else echo "SendMail is NOT running" fi #测试机上面这些服务都没开启 qing@qingsword.com:~$ sh port.sh WWW is NOT running FTP is NOT running SSH is NOT running SendMail is NOT running
if进行文件操作实例,下面是shell检测文件和文件夹的常用参数
#-e检测是否存在某文件或文件夹 #-f检测是否为文件 #-d检测是否为文件夹 qing@qingsword.com:~$ vim ifcreate.sh #!/bin/bash #by www.qingsword.com #在脚本运行当前目录创建删除文件或文件夹 if [ ! -e logical ] ; then #如果不存在logical touch logical #创建文件logical echo "Just make a file logical" exit 1 #自定义的返回值 elif [ -e logical ] && [ -f logical ] ; then #如果存在并且是一个文件 rm logical #删除 mkdir logical #创建同名文件夹 echo "remove file logical" echo "and make directory logical" exit 1 elif [ -e logical ] && [ -d logical ] ; then #如果存在并且是一个文件夹 rm -rf logical #删除文件夹 echo "remove directory logical" exit 1 #默认exit 0,是成功退出,而exit 1,exit 2或者其他自定义数值一般用作不成功退出返回给程序调用者 else echo "Does here have anything?" #似乎永远不会运行到 fi #测试 qing@qingsword.com:~$ sh ifcreate.sh Just make a file logical qing@qingsword.com:~$ sh ifcreate.sh remove file logical and make directory logical qing@qingsword.com:~$ sh ifcreate.sh remove directory logical
bash shell的语法和各大编程语言大同小异,唯一比较恶心的就是空格,有时候中括号或等号和变量之间忘记空格都会报错。
0×5.case实例
case相当于一个并列的if.else,请看下面的实例:
#提供一个选择列表,通过传入的值判断执行列表中哪一部分的内容 qing@qingsword.com:~$ vim casesh.sh #!/bin/bash #case demo #by www.qingsword.com echo "This program will print your selection" case $1 in #读取传递给脚本的第一个参数 one) echo "Your choice is $1" ;; two) echo "Your choice is $1" ;; three) echo "Your choice is $1" ;; *) #如果不是上面三个值,进入这个分支 echo "Usage {one|two|three}" exit 1 #自定义的程序返回值 esac #测试不携带值与携带值返回的结果 qing@qingsword.com:~$ sh casesh.sh This program will print your selection Usage {one|two|three} qing@qingsword.com:~$ sh casesh.sh two This program will print your selection Your choice is two
0×6.for循环实例
下面这段shell实现循环累加,从1到100,最后输出结果
qing@qingsword.com:~$ vim fordemo.sh #!/bin/bash #for demo #by www.qingsword.com declare -i total #定义一个整数 for ((i=1;i<=100;i=i+1)) #满足条件i<=100时继续循环 #或for ((i=1;i<=100;i++)) do total=total+i #递增 #或total+=i done echo "The count is $total" #测试,算法就是(1+100)*100/2=5050 qing@qingsword.com:~$ bash fordemo.sh The count is 5050
for循环遍历列表:
qing@qingsword.com:~$ vim forindemo.sh #!/bin/bash #for in demo #by www.qingsword.com LIST="Tom John George Smith" #人名列表 for i in $LIST #枚举列表中的值打印到屏幕 do echo $i done qing@qingsword.com:~$ sh forindemo.sh Tom John George Smith
输出系统所有账户名称列表:
qing@qingsword.com:~$ vim accountdemo.sh #!/bin/bash #account display #by www.qingsword.com #用':'符号分割/etc/passwd文件并取第一个字段,然后sort排序 account=`cut -d ":" -f1 /etc/passwd|sort` echo "The following is your linux server's account:" for i in $account do echo $i done qing@qingsword.com:~$ sh accountdemo.sh The following is your linux server's account: avahi avahi-autoipd backup bin ...
0×7.while循环实例
用while循环实现从1加到100:
qing@qingsword.com:~$ vim whiledemo.sh #!/bin/bash #while demo #by www.qingsword.com declare -i i=0 declare -i s=0 #当i不等于100时继续循环 while [ $i != 100 ] do i=i+1 #递增i s=s+i #递增s,每次循环加上i的当前值 done echo "The count is $s" #包含declare就需要用bash执行 qing@qingsword.com:~$ bash whiledemo.sh The count is 5050
0×8.until循环实例
until实现1加到100:
qing@qingsword.com:~$ vim untildemo.sh #!/bin/bash #until demo #by www.qingsword.com declare -i i=0 declare -i s=0 until [ $i = 100 ] #当i等于100时退出循环 do i=i+1 s=s+i done echo "The count is $s" qing@qingsword.com:~$ bash untildemo.sh The count is 5050
until循环读取键盘值:
qing@qingsword.com:~$ vim untiltwo.sh #!/bin/bash #by www.qingsword.com #当用户键入y或者Y时退出循环,until是满足条件则退出循环,for和while是满足条件继续循环 until [ "$yn" = "y" ] || [ "$yn" = "Y" ] do echo "Press y/Y to stop:" read yn done echo "Stop here." qing@qingsword.com:~$ sh untiltwo.sh Press y/Y to stop: asdf Press y/Y to stop: 23r Press y/Y to stop: y Stop here.
0×9.shell脚本调试
使用sh命令调试脚本,最常用的参数如下:
-n 参数是不执行脚本,查询脚本语法,有错误则显示
-v 在执行脚本之前,先将脚本的内容显示在屏幕上
-x 将脚本的执行过程显示出来,脚本语句前面添加+号,而在屏幕上的输出则不添加+号
qing@qingsword.com:~$ bash -x untildemo.sh + declare -i i=0 + declare -i s=0 + '[' 0 '!=' 100 ']' + i=i+1 + s=s+i ...... #会将程序执行的所有过程都显示出来 ...... + '[' 99 '!=' 100 ']' + i=i+1 + s=s+i + '[' 100 '!=' 100 ']' + echo 'The count is 5050' The count is 5050