shell script是利用shell的功能所写的一个“程序”,这个程序是使用纯文本文件,将一些shell的语法与命令(含外部命令)写在里面,搭配正则表达式、管道命令与数据流重定向等功能,以达到我们所想要的处理目的。
一,第一个shell script
1 2 3 4 5 6 7 8 |
#!/bin/bash #Program: # This program shows "Hello World!" in your screen. #History: # 2016/9/30 Leo First release echo -e "Hello World! \a \n" exit 0 |
(1)第一行#!/bin/bash声明这个script使用的shell名称:因为我们使用的是bash,所以必须以“#!/bin/bash”来声明这个文件内的语法使用bash的语法。那么当这个程序被执行时,它就能加载bash相关环境配置文件,并且执行bash来使我们下面的命令能够执行。
(2)程序内容说明:除了第一行的#!是用来声明shell的之外,其他的#都是“批注”用途。一般来说,建议养成说明该script的内容与功能、版本信息、作者与联络方式、建立日期等习惯。
1.交互式脚本:变量内容由用户决定
1 2 3 4 5 6 7 8 9 |
#!/bin/bash # Program: # User inputs his first name and last name. Program shows his full name. # History: # 2016/9/30 Leo Firat release read -p "Please input your first name: " firstname #提示用户输入 read -p "Please input your last name: " lastname #提示用户输入 echo -e "\nYour full name is : $firstname $last name" #结果由屏幕输出 |
输出:
1 2 3 4 5 |
[root@VM_198_209_centos shell]# sh 02read.sh Please input your first name: Leo Please input your last name: Lee Your full name is :Leo Lee |
2.数值运算:简单的加减乘除
我们可以使用declare来定义变量的类型,此外,我们也可以利用$((计算式))来进行数值运算。可惜的是,bash shell只支持整数是数据哦!
1 2 3 4 5 6 7 8 9 10 11 |
#!/bin/bash # Program: # User inputs 2 integer numbers;program will cross these two numbers. # History: # 2016/9/30 Leo First release echo -e "You Should input 2 numbers, I will cross them! \n" read -p "first number: " firstnu read -p "second number: " secnu total=$(($firstnu*$secnu)) #切记等号两边不能有空格 echo -e "\nThe result of $firstnu * $secnu is : $total" |
输出:
1 2 3 4 5 6 7 |
[root@VM_198_209_centos shell]# sh 03math.sh You Should input 2 numbers, I will cross them! first number: 2 second number: 3 The result of 2 * 3 is : 6 |
(1)在数值运算上,我们可以使用“declare -i total=$firstnu*$secnu”,也可以使用上面的方式来进行。即:var=$((运算内容))
(2)上述倒数第二行=号两边不能有空格,否则将运行如下错误:
1 2 3 4 5 6 7 8 |
[root@VM_198_209_centos shell]# sh 03math.sh You Should input 2 numbers, I will cross them! first number: 2 second number: 3 03math.sh: line 10: total: command not found The result of 2 * 3 is : |
二,使用判断式
前面我们提过$?(成功返回0,失败返回非0)这个变量代表的意义,并且介绍过&&和||,那么如果我们想要判断一个目录是否存在,可以使用”test”命令。
test -e /dmtsai && echo “exists” || echo “Not exists” #检查/dmtsai是否存在
1.test的用法如下:(也可man test查看帮助手册)
(1)关于某个文件名的“文件类型”的判断,如test -e filename表示是否存在
-e:该文件名是否存在(常用)
-f:该文件名是否存在且为文件(常用)
-d:该文件名是否存在且为目录(常用)
-b:该文件名是否存在且为一个block device设备
-c:该文件名是否存在且为一个character device设备
-S:该文件名是否存在且为一个Socket文件
-p:该文件名是否存在且为一个FIFO(pipe)文件
-L:该文件名是否存在且为一个连接文件
(2)关于文件的权限检测,如test -r filename 表示可读否
-r:检测该文件名是否存在且具有“可读”的权限
-w:检测该文件名是否存在且具有“可写”的权限
-x:检测该文件名是否存在且具有“可执行”的权限
-u:检测该文件名是否存在且具有“SUID”的属性
-g:检测该文件名是否存在且具有“SGID”的属性
-k:检测该文件名是否存在且具有“Sticky bit”的属性
-s:检测该文件名是否存在且为“非空白文件”
(3)两个文件之间的比较,如:test file1 -nt file2
-nt:(newer than)判断file1是否比file2新
-ot:(older than)判断file1是否比file2旧
-et:判断file1与file2是否为同一个文件,可用在判断hard link的判定上。主要意义在于判定两个文件是否均指向同一个inode
(4)关于两个整数之间的判定,例如test n1 -eq n2
-eq:两数值相等
-ne:两数值不等,可用于判断上一个脚本执行是否正确:if[ $? -ne 0 ] …
-gt:n1大于n2(greater than)
-lt:n1小于n2(less than)
-ge:n1大于等于n2(greater than or equal)
-le:n1小于等于n2(less than or equal)
2.利用判断符号[ ]
例如判断$HOME是否为空,可以这样做:
1 |
[ -z "$HOME" ] ; echo $? |
注意:在bash的语法当中使用中括号作为shell的判断式时,必须要注意中括号的两端需要有空格符来分隔!
1 |
[ "$HOME" == "$MAIL" ] #判断两个字符串是否相同 |
使用[ ]判断符号需要注意:
(1)在中括号内的每个组件都需要有空格键来分隔(包括==号,如下面的示例)
(2)在中括号内的变量,最好都以双引号括起来
(3)在中括号内的常量,最好都以单或双引号括起来
3.shell script的默认变量($0, $1)
script针对参数已经有设置好一些变量的名称了,对应对下:
1 2 3 |
#执行的脚本文件名为$0, 第一个接的参数是$1... /path/to/scriptname opt1 opt2 opt3 opt4 $0 $1 $2 $3 $4 |
4.条件判断式
(1)if…then
1 2 3 |
if [ 条件判断式 ]; then 当条件成立时,可以进行的命令工作内容 fi |
1 2 3 4 5 6 7 8 |
#多个条件判断(if...elif...else)分多种不同情况执行 if [ 条件判断式一 ]; then 当条件判断式一成立时,可以进行的命令工作内容 elif [ 条件判断式二 ]; then 当条件判断式二成立时,可以进行的命令工作内容 else 当条件判断式一,二都不成立时,可以进行的命令工作内容 fi |
举个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#!/bin/bash #Program: # This program shows the user's choice #History: # 2016/10/7 Leo First release read -p "Please input (Y/N): " yn if [ "$yn" == "Y" ] || [ "$yn" == "y" ]; then echo "OK, continue" elif [ "$yn" == "N" ] || [ "$yn" == "n" ]; then echo "Oh, interrupt!" else echo "I don't konw what your choice is" fi |
netstat命令可以查询到目前主机打开的网络服务端口(service ports),我们可以利用“netstat -tuln”来取得目前主机有启动的服务。(其中t表示tcp,u表示udp,l表示linstening, n表示numeric即将端口用数字表示,如http–>80)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#!/bin/bash echo "Now, I will detect your Linux server's services!" echo -e "The www, ftp, ssh, and mail will be detect!\n" #-e是解释转义符的作用 testing=$(netstat -tuln | grep ":80") #检测port 80是否存在 if [ "$testing" != "" ]; then echo "WWW is running in your system." fi testing=$(netstat -tuln | grep ":22") #检测port 22是否存在 if [ "$testing" != "" ]; then echo "SSH is running in your system." fi testing=$(netstat -tuln | grep ":21") #检测port 21是否存在 if [ "$testing" != "" ]; then echo "FTP is running in your system." fi testing=$(netstat -tuln | grep ":25") #检测port 25是否存在 if [ "$testing" != "" ]; then echo "Mail is running in your system." fi |
输出:
1 2 3 4 5 6 7 |
[root@VM_198_209_centos shell]# sh 05test.sh Now , I will detect your Linux server's services! The www, ftp, ssh, and mail will be detected! www is running in your system. ftp is running in your system. ssh is running in your system. |
(2)case…esac
1 2 3 4 5 6 7 8 9 10 11 12 |
case $变量名称 in #关键字为case,还有变量前有$ "第一个变量内容") #每个变量内容建议用双引号括起来,关键字为小括号) 程序段 ;; #每个类型结尾使用两个连续的分号来处理 "第二个变量内容") 程序段 ;; *) #最后一个变量内容都会用*来代表所有其他数值 其他变量内容执行的程序段 exit 1 ;; esca #最终以esca结尾 |
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#!/bin/bash echo "This program will print your selection." case $1 in "one") echo "Your choice is ONE" ;; "two") echo "Your choice is TWO" ;; "three") echo "Your choice is THREE" ;; *) echo "Usage $0 {one|two|three}" ;; esac |
输出:
1 2 3 |
[root@VM_198_209_centos shell]# sh 06case.sh one This program will print your selection. Your choice is ONE |
(3)利用function功能
简单地说,函数可以在shell script当中做出一个类似自定义执行命令的东西,最大的功能是,可以简单我们很多的程序代码。function的语法如下:
1 2 3 |
function fname (){ 程序段 } |
将上面的例子改写如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#!/bin/bash function printit (){ echo -n "Your choice is " #加上-n可以不断行继续在同一行显示 } echo "This program will print your selection." case $1 in "one") printit; echo $1 | tr 'a-z' 'A-Z' #将参数做大小写转换 ;; "two") printit; echo $1 | tr 'a-z' 'A-Z' ;; "three") printit; echo $1 | tr 'a-z' 'A-Z' ;; *) echo "Usage $0 {one|two|three}" ;; esac |
输出:
1 2 3 |
[root@VM_198_209_centos shell]# sh 07fun.sh one This program will print your selection. Your choice is ONE |
5.循环(loop)
(1)不定循环:while do done, until do done
1 2 3 4 5 6 7 8 9 10 11 |
while [ condition ] #中括号内的状态就是判断式 do 程序段落 done #另外一种不定循环的方式,与while相反 #当condition条件成立时,就终止 until [ condition ] do 程序段落 done |
假设我们要让用户输入yes或YES才结束程序的执行,否则就一直进行告知用户输入字符串:
1 2 3 4 5 6 7 |
#!/bin/bash while [ "$yn" != "yes" -a "$yn" != "YES" ] do read -p "Please input yes/YES to stop this program: " yn done echo "OK! you input the correct answer." |
如果使用until,则如下:
1 2 3 4 5 6 7 |
#!/bin/bash until [ "$yn" == "yes" -o "$yn" == "YES" ] do read -p "please input yes/YES to stop this program: " yn done echo "OK, you input the correct answer." |
再如,计算1+2+3+…+100
1 2 3 4 5 6 7 8 9 10 |
#!/bin/bash s=0 #这是累加的数值变量 i=0 #这是累计的数值,1,2,3,... while [ "$i" != "100" ] do i=$( ($i+1) ) #每次i都会增加1 s=$( ($s+$i) ) #每次都会累加一次 done echo "The result of '1+2+...+100' is: $s" |
输出:
1 2 |
[root@VM_198_209_centos shell]# sh 10add.sh The result of '1+2+...+100' is: 5050 |
(2)固定循环:for…do…done
相对于while,until的循环方式是必须要“符合某个条件”的状态,for这种语法则是“已经知道要进行几次循环”的状态,它的语法如下:
1 2 3 4 |
for var in con1 con2 con3 ... do 程序段 done |
例一:有三种动物,每一行都输出“There are dogs…”之类的字样
1 2 3 4 5 6 |
#!/bin/bash for animal in dog cat elephant do echo "There are ${animal}s..." done |
注意,dog->dogs,这里我们需要在变量的后面加上s,该语句若这样写是显然不对的:echo “There are $animals…”
所以这里使用了{ }的写法
例二:由于系统上面的各种账号都写在/etc/passwd内的第一个字段,所以可以通过管道命令cut找出单纯的账号名称后,以id及finger分别检查用户的标识符与特殊参数
1 2 3 4 5 6 7 8 |
#!/bin/bash users=$(cut -d ':' -f1 /etc/passwd) #获取账号名称 for username in $users #开始循环 do id $username finger $username done |
输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
[root@VM_198_209_centos shell]# sh 12for.sh uid=0(root) gid=0(root) groups=0(root) Login: root Name: root Directory: /root Shell: /bin/bash On since Mon Dec 26 09:01 (CST) on pts/0 from 202.121.223.10 1 hour 53 minutes idle On since Mon Dec 26 09:13 (CST) on pts/1 from 202.121.223.10 1 hour 49 minutes idle On since Mon Dec 26 11:12 (CST) on pts/2 from 58.40.126.205 5 seconds idle On since Mon Dec 26 11:17 (CST) on pts/3 from 202.121.223.10 14 minutes 5 seconds idle New mail received Wed Mar 23 18:00 2016 (CST) Unread since Tue Mar 22 20:00 2016 (CST) No Plan. ... ... |
for…do…done的数值处理,除了上述方法之外,for循环还有另外一种写法:
1 2 3 4 |
for ( (初始值;限制值;执行步长) ) do 程序段 done |
例子如下:
1 2 3 4 5 6 7 8 9 10 |
#!/bin/bash read -p "Please input a number, I will count for 1+2+...your_input:" nu s=0; for ( (i=1;i<=$nu;i=i+1) ) do s=$( ($s+$i) ) done echo "The result of '1+2+...+$nu' is : $s" |
输出:
1 2 3 |
[root@VM_198_209_centos shell]# sh 13for.sh Please input a number, I will count for 1+2+...your input: 100 The result of '1+2+...+100' is : 5050 |
其中几点注意:
1)nu前面需要有一个空格;
2)for循环有两层括号;
3)数值计算,for循环体也要有两层括号。
参考:
Linux私房菜
man手册