本文将结合前面介绍的Linux命令、管道符等知识,经过VI编辑器编写Shell脚本,实现能自动化工做的脚本文件。linux
在讲解Linux经常使用命令“linux入门系列5--新手必会的linux命令”一文中已提到,Shell终端解释器是人机交互的桥梁,是用户与内核之间的翻译官。它做为用户与Linux系统内部通讯的媒介,为用户提供了一个面向Linux内核发送请求以便运行处横向的界面系统级程序,用户能够经过Shell启动、挂起、中止程序。shell
实际上Shell不只是一个命令行解释器,除了能经过它控制程序的启动、中止、挂起等操做外,它仍是一个强大的编程语言,易编写、易调试、灵活性强。Shell是解释执行的脚本语言,能够调用Linux系统命令。express
Shell除了可以支持各类变量和参数外,还提供了诸如循环、分支等高级编程语言才有的控制特性。要想正确使用这些特性,准确下达命令尤其重要,在讲解具体编程语法以前,先学习一下Shell脚本的执行方式。编程
Shell脚本命令的工做方式有两种:交互式和批处理。vim
经过SHELL环境变量,能够查看当前系统默认使用的终端解释器,默认采用Bash解释器数组
[root@heimatengyun ~]# echo $SHELL
/bin/bash
复制代码
在Shell脚本中不只会用到前文学习过的Linux命令、管道符、数据流重定向等语法规则,还须要把内部功能模块化后经过逻辑语句进行处理,最终造成平常所见的shell脚本。bash
约定俗成的全部编程语言第一个程序都是Hello World,咱们用Shell编写一个简单的脚本编程语言
[root@heimatengyun test]# ls
test1.txt test2.txt
[root@heimatengyun test]# echo "hello world"
hello world
[root@heimatengyun test]# vi hello.sh
#!/bin/bash
#author:heimatengyun
echo " hello world"
复制代码
保存并退出。Shell脚本文件能够是任意名称,可是为了不被误认为是普通文件,建议后缀采用.sh,以表示是一个脚本文件。编辑器
ps:第一行#!/bin/bash是脚本声明,用来告诉系统使用哪一种Shell解释器来执行脚本。模块化
第二行#author:heimatengyun是注释信息,对脚本功能进行介绍方便往后维护。
第三行echo " hello world"是命令语句,能够是简单的各类linux命令,也能够是经过各类逻辑语句和语法规则组合的复杂语句块。
Shell编程格式就这么简单,我称之为Shell编程“三段式”,之后编码就按照这个格式编写便可。
Linux中执行脚本有两种方式:用解释器直接执行、经过脚本路径执行。
语法:bash或sh 脚本名称
案例:执行前文建立的hello.sh脚本文件
[root@heimatengyun test]# bash hello.sh
hello world
[root@heimatengyun test]# sh hello.sh
hello world
复制代码
语法:脚本的绝对路径或相对路径
案例:执行前文建立的hello.sh脚本文件
[root@heimatengyun test]# /root/test/hello.sh
-bash: /root/test/hello.sh: Permission denied
[root@heimatengyun test]# ./hello.sh
-bash: ./hello.sh: Permission denied
复制代码
纳尼?提示权限不足。是的,你没看错,若是经过路径这种方式运行,须要修改文件的执行权限,(默认的权限不能够执行,而且在数据脚本路径时按tab键也无补全提示)文件权限相关命令将在后续文章中继续讲解,此处先按照以下命令添加脚本的可执行权限便可。
[root@heimatengyun test]# ll
total 12
-rw-r--r--. 1 root root 4 Dec 1 09:48 hello.sh
[root@heimatengyun test]# chmod 777 hello.sh
[root@heimatengyun test]# ll
total 8
-rwxrwxrwx. 1 root root 53 Dec 1 09:22 hello.sh
复制代码
再次执行,此时输入路径在按tab键也会提示自动补全了
[root@heimatengyun test]# ./hello.sh
hello world
[root@heimatengyun test]# /root/test/hello.sh
hello world
复制代码
掌握Shell脚本运行方式后,咱们正式开始进入Shell编程语法学习。
任何一门语言的学习都没有速成方法,都离不开大量的练习,只有多敲才能更加熟练,才能理解的更加深入。
Linux Shell(此处为默认bash)中的变量分为系统变量和用户自定义变量,系统变量包括PWD、
USER等,用户自定义变量则为用户根据实际须要自定义的变量。
能够经过set命令查看Shell中全部变量。
[root@heimatengyun test]# set
BASH=/bin/bash
...省略部份内容
复制代码
下文主要演示自定义变量
语法:
变量名=值
变量命名规则:
(1)变量名能够由字母、数字和下划线注册,但不能以数字开头
(2)等号两侧不能有空格
(3)变量名通常大写
案例:
(1)定义变量并使用
[root@heimatengyun test]# SRT="wellcome"
[root@heimatengyun test]# echo $SRT
wellcome
[root@heimatengyun test]# set |grep SRT
SRT=wellcome
[root@heimatengyun test]# env |grep SRT
[root@heimatengyun test]#
复制代码
能够看到自定义变量SRT,能够经过set命令查询出来。
(2)变量提高为全局环境变量
[root@heimatengyun test]# export SRT
[root@heimatengyun test]# env |grep SRT
SRT=wellcome
复制代码
上例中自定义的变量SRT经过env命令查看,并未在环境变量中查询出来,经过export将自定义变量SRT提高为环境变量后,就能够经过env查询出来
(3)撤销变量
[root@heimatengyun test]# unset SRT
[root@heimatengyun test]# echo $SRT
[root@heimatengyun test]# set |grep SRT
[root@heimatengyun test]# env |grep SRT
复制代码
撤销变量使用unset命令,撤销以后变量将不存在
除了直接赋值,还能够将命令执行的结果赋值给变量
语法:
变量=`命令` 或 变量=$(命令)
说明:
命令用反引号或$()包含起来,先执行命令而后将命令执行结果赋值给变量。
案例:
[root@heimatengyun test]# ls
hello.sh test1.txt test2.txt
[root@heimatengyun test]# RESULT=`ls`
[root@heimatengyun test]# echo $RESULT
hello.sh test1.txt test2.txt
复制代码
位置参数变量主要用于取脚本的参数值,语法以下
变量名称 | 功能 |
---|---|
$n | n为数字,$0表示命令自己,![]() |
$* | 表示命令行中全部的参数,把参数当作一个总体 |
$@ | 表示命令行中全部的参数,把每一个参数区分对待 |
$# | 表示命令行中全部参数的个数 |
案例:
输入2个参数,计算俩个数的和并打印输出
[root@heimatengyun test]# vi sum.sh
#sum
#!/bin/bash
#分别接收2个参数
num1=$1
num2=$2
#求和
sum=$(($num1+$num2))
#打印
echo $sum
复制代码
保存并退出,执行脚本输入2个数查看执行结果
[root@heimatengyun test]# bash sum.sh 1 2
3
复制代码
预约义变量都有特殊的做用,参看下表
变量名 | 功能 |
---|---|
$? | 表示***当前进程中***最后一条命令直接的返回状态。0:执行成功;非0:执行失败 |
$$ | 当前进程号(PID) |
$! | ***后台运行***的最后一个进程的进程号(PID) |
案例:
(1)查看当前进程和后台运行的最后一个进程
[root@heimatengyun test]# vi mypid.sh
#!/bin/bash
#输出当前进程PID,也就是当前脚本运行时生成的PID
echo "当前进程PID=$$"
echo "最后一个后台进程PID=$!"
复制代码
保存退出,执行脚本
[root@heimatengyun test]# bash mypid.sh
当前进程PID=7810
最后一个后台进程PID=
复制代码
能够看到$$和 !表示是后台最有运行的一个进程的PID。
(2)输出当前进程PID,并查看上一次命令执行结果
[root@heimatengyun test]# vi pid.sh
#!/bin/bash
#输出当前进程PID,也就是当前脚本运行时生成的PID
echo "当前进程PID=$$"
#经过ls命令,查找不存在的文件,&表示让命令后台执行
ls -l XXX.txt&
echo "最后一个进程PID=$!"
echo "最后一条命令执行结果:$?"
复制代码
保存退出,执行脚本
[root@heimatengyun test]# bash pid.sh
当前进程PID=7395
最后一个进程PID=7396
最后一条命令执行结果:0
[root@heimatengyun test]# ls: cannot access XXX.txt: No such file or directory
[root@heimatengyun test]#
复制代码
能够看到命令执行的进程和当前脚本运行的进程不是同一个,而且虽然xxx.txt文件不存在,但结果仍然返回为0。若是改成查找一个已经存在的文件,毋庸置疑,返回结果确定仍然为0。也就是说上边脚本无论命令执行是否成功,都将返回0,缘由是经过&让命令后台执行,其实是新开了一个进程,而$?只能获取到当前进程的最后一次执行命令结果,所以在作判断命令是否执行成功是特别要当心。
语法:
(1)***[运算式]***
(2)expr m + n 注意expr运算符间要有空格
案例:
(1)采用$(())实现两数相加
[root@heimatengyun test]# S=$((2+3))
[root@heimatengyun test]# echo $S
5
复制代码
(2)采用$[]实现两数相加
[root@heimatengyun test]# SUM=$[2+3]
[root@heimatengyun test]# echo $SUM
5
复制代码
(3)采用expr命令实现两数相加
[root@heimatengyun test]# S1=`expr 2 + 3`
[root@heimatengyun test]# echo $S1
5
复制代码
注意expr命令时,操做符之间必定要有空格,不然执行的不是计算,而是字符串链接,以下示例演示了有空格和无空格的区别,另外乘法符号*因为与通配符冲突,所以须要用\转义
[root@heimatengyun test]# expr 2 + 3
5
[root@heimatengyun test]# expr 2+3
2+3
[root@heimatengyun test]# expr 2\*3
2*3
[root@heimatengyun test]# expr 2 \* 3
6
[root@heimatengyun test]# expr 2 * 3
expr: syntax error
复制代码
(4)采用expr命令计算“2加3的和乘以5”
[root@heimatengyun test]# S2=`expr 2 + 3`
[root@heimatengyun test]# echo $S2
5
[root@heimatengyun test]# expr $S2 \* 5
25
复制代码
注意操做符之间的空格,以上为分步计算,也能够直接一步计算,多层嵌套是注意须要转义反引号,以下:
[root@heimatengyun test]# expr `expr 2 + 3` \* 5
25
[root@heimatengyun test]# echo `expr \`expr 2 + 3\` \* 5`
25
复制代码
语法:[ 条件 ]
说明:条件先后必需要有空格,非空返回true,可使用$?验证(0:true,非0:false)
案例:
(1)分别判断存在和不存在的变量,检验返回值
[root@heimatengyun test]# [ $HOME ]
[root@heimatengyun test]# echo $?
0
[root@heimatengyun test]# [ $TEST ]
[root@heimatengyun test]# echo $?
1
复制代码
因为$HOME是环境变量确定存在,所以返回0,表示条件知足,变量不为空;而TEST变量因为没定义,因此不存在,返回非0。
(2)条件判断语句用于逻辑判断
[root@heimatengyun test]# [ $HOME ]&& echo ok || echo notok
ok
[root@heimatengyun test]# [ $TEST ]&& echo ok || echo notok
notok
复制代码
相似于其余语言中的三元运算符,条件知足则执行紧随其后的语句,不然不执行。
符号 | 含义 |
---|---|
= | 字符串比较 |
-lt | 小于 |
-le | 小于等于 |
-eq | 等于 |
-gt | 大于 |
-ge | 大于等于 |
-ne | 不等于 |
符号 | 含义 |
---|---|
-r | 有读的权限 |
-w | 有写的权限 |
-x | 有执行的权限 |
符号 | 含义 |
---|---|
-f | 文件存在且是一个常规文件 |
-d | 文件存在而且是一个目录 |
-e | 文件存在 |
(1)整数比较
[root@heimatengyun test]# [ 1 -gt 2 ]
[root@heimatengyun test]# echo $?
1
复制代码
1不大于2,因此输出1,表示false
(2)文件类型判断
[root@heimatengyun test]# [ -f test1.txt ]
[root@heimatengyun test]# echo $?
0
[root@heimatengyun test]# [ -f xxxx.txt ]
[root@heimatengyun test]# echo $?
1
复制代码
test1.txt文件存在,输出0表示true;而xxxx.txt文件不存在,因此输出1表示false
(3)文件权限判断
[root@heimatengyun test]# ll
-rw-r--r--. 1 root root 9 Nov 30 20:43 test1.txt
[root@heimatengyun test]# [ -x test1.txt ]
[root@heimatengyun test]# echo $?
1
复制代码
因为test1.txt 无执行权限,所以返回1,表示false
流程控制结构分为:顺序结构、分支结构、循环结构。顺序结构根据语句依次顺序执行,分支结构根据条件判断运行不一样的分支,循环结构则根据条件判断是否循环执行。
分支语句分为:if判断语句、case语句
语法:
if [ 判断条件 ]
then
程序
fi
或者then写到判断条件以后,用逗号隔开,等效于上边语句
if [ 判断条件 ];then
程序
fi
if判断还能够多层嵌套,形如:
if [ 判断条件1 ]
then
程序1
elif [ 判断条件2 ]
then
程序2
else
程序3
fi
说明:条件判断语句中,注意***前中括号与if之间必须有空格,中括号先后也必须都有空格***
案例:
从键盘输入年龄,根据年龄返回不一样的形容语言
[root@heimatengyun test]# vim if.sh
#!/bin/bash
read -p "请输入您的年龄:" AGE
if [ $AGE -le 18 ];then
echo "未成年"
elif [ $AGE -le 30 ];then
echo "年轻气盛"
else
echo "糟老头子"
fi
复制代码
使用vim编辑器编辑内容(此处使用vim是由于vim比vi更适合在编程环境使用,有错误提示等功能,建议编写脚本都采用vim),保存退出并执行脚本
[root@heimatengyun test]# bash if.sh
请输入您的年龄:16
未成年
[root@heimatengyun test]# bash if.sh
请输入您的年龄:40
糟老头子
复制代码
此处使用了read命令读取键盘输入,-p参数表示显示提示符内容。
语法:
case $变量 in
" 值1")
语句1
;;
" 值2")
语句2
;;
*)
语句
;;
esac
案例:
根据传入脚本的参数分别输出一、二、3对应的英文
[root@heimatengyun test]#vim case.sh
#!/bin/bash
case $1 in
1)
echo one
;;
2)
echo two
;;
3)
echo three
;;
*)
echo "error"
;;
esac
复制代码
保存退出并执行
[root@heimatengyun test]# bash case.sh 1
one
[root@heimatengyun test]# bash case.sh 2
two
[root@heimatengyun test]# bash case.sh 3
three
[root@heimatengyun test]# bash case.sh 8
error
复制代码
循环语句分为:for循环、while循环
for循环有两种语法格式,分别以下:
语法1:
for 变量 in 值1 值2 值3...
do
程序
done
语法2:
for ((初始值;循环控制条件;变量变化))
do
程序
done
案例:
(1)一天三次问好
[root@heimatengyun test]#vim greeting.sh
#!/bin/bash
for time in morning afternoon evening
do
echo "good $time"
done
复制代码
保存并退出,执行问候脚本,输出问候语句
[root@heimatengyun test]# bash greeting.sh
good morning
good afternoon
good evening
复制代码
(2)用for循环求1累加到5的和
[root@heimatengyun test]# vim getsum.sh
#!/bin/bash
sum=0
for ((i=0;i<=5;i++))
do
sum=$(($i+$sum))
done
echo "sum=$sum"
复制代码
保存退出,并执行脚本
[root@heimatengyun test]# bash getsum.sh
sum=15
复制代码
语法:
while [ 条件判断式 ]
do
程序
done
案例:
(1)用while循环求1累加到5的和
[root@heimatengyun test]# vim while.sh
#!/bin/bash
i=1
sum=0
while [ $i -le 5 ]
do
sum=$[$i+$sum]
i=$[$i+1]
done
echo "sum=$sum"
复制代码
保存并执行
[root@heimatengyun test]# bash while.sh
sum=15
复制代码
注意:
一、是给变量赋值,左边不用加
[
i=
i+1],不然报错“command not found”
二、条件判断语句注意左中括号与关键字之间、括号内部首位、操做符与操做数之间都有空格
三、i=
i+1],若是错写为i=$i+1则会报错“integer expression expected”。
函数是对功能的封装,能够提供代码重用性。函数分为系统函数和用户自定义函数,对于系统函数直接拿来使用便可,自定义函数则是根据具体需求编写。
使用现有的系统函数能够提升工做效率,因为篇幅所限,只简单介绍两个与文件路径相关的系统函数,具体使用方法也能够经过前面介绍的man命令来查询函数具体的用法。
语法:
basename [文件路径或字符串] [后缀]
功能描述:
删掉全部的前缀,包括最后一个/,而后打印字符串;指定了后缀则在此基础上再去掉后缀。
案例:
[root@heimatengyun test]# basename "sdf/sdf/sdf"
sdf
[root@heimatengyun test]# basename /root/test/test1.txt
test1.txt
[root@heimatengyun test]# basename /root/test/test1.txt txt
test1.
[root@heimatengyun test]# basename /root/test/test1.txt .txt
test1
复制代码
语法:
dirname 文件绝对路径
功能描述:
从给定的包含绝对路径的文件名中去除文件名,而后返回生效的路径。简单说就是去掉非目录部分,返回保留目录部分,但不包含最后的/
案例:
[root@heimatengyun test]# dirname /root/test/test1.txt
/root/test
复制代码
语法:
[function] 函数名[()]
{
语句;
[return 返回值;]
}
说明:
(1)shell是解释执行而非编译执行,所以语句是逐行执行的,必须在函数调用以前先声明函数。
(2)函数返回值只能经过$?系统变量获取。能够显示添加return语句返回(返回值范围为0-255),不然将以最后一条命令运行结果做为返回值返回。
方括号内的部分function、参数、返回值能够是可选的。function show() 和function show和show()这几种形式都是能够的。
案例:
(2)输入1个整数,打印出比输入小的整数
[root@heimatengyun test]# vim function.sh
#!/bin/bash
function printNumber()
{
i=0;
while [ $i -lt $1 ]
do
echo $i;
i=$[$i+1];
# i=`expr \`expr $i + 1 \``
sleep 1;
done
return 0;
}
read -p "请输入一个数:" n;
printNumber $n;
复制代码
保存文件并执行脚本
[root@heimatengyun test]# bash function.sh
请输入一个数:5
0
1
2
3
4
复制代码
经过上边的演示,基本已经会编写简单的shell脚本了,本小节讲解shell脚本接收用户参数以及用户参数的判断。
以前提到过对linux命令来讲,可否根据须要采用各类参数组合来完成特定的功能才是衡量命令是否掌握的标准。一样,函数或脚本也须要与用户交互,能灵活处理用户参数。
前面2.3中中已经提到几个shell内设的用于接收参数的变量:*,
#
下面用案例进行演示
[root@heimatengyun test]# vim para.sh
#!/bin/bash
echo "当前脚本名称:$0"
echo "总共有$#个参数,分别为:$*"
echo "第一个参数为:$1,第三个参数为:$3"
复制代码
保存脚本,并执行
[root@heimatengyun test]# bash para.sh 1 2 3
当前脚本名称:para.sh
总共有3个参数,分别为:1 2 3
第一个参数为:1,第三个参数为:3
复制代码
能够看到,脚本名称后直接带参数,参数之间用空格隔开,在脚本内部直接能够获取到各个位置上的参数
shell脚本中条件判断语句能够用来判断表达式是否成立,若条件成立返回0表示true,不然返回其余非0随机数表示false。按判断对象的不一样,能够分为如下四种判断语句:文件测试语句、逻辑测试语句、整数值比较语句、字符串比较语句。
文件测试便是用指定的条件判断文件是否存在或权限是否知足,文件测试具体参数以下
运算符 | 做用 |
---|---|
-d | 测试文件是否为目录类型 |
-e | 测试文件是否存在 |
-f | 判断是否为通常文件 |
-r | 测试当前用户是否有读权限 |
-w | 测试当前用户是否有写权限 |
-x | 测试当前用户是否有执行权限 |
案例:
判断是否为目录类型的文件
[root@heimatengyun test]# [ -d /root/test ]
[root@heimatengyun test]# echo $?
0
[root@heimatengyun test]# [ -d /root/test/test1.txt ]
[root@heimatengyun test]# echo $?
1
复制代码
先经过条件测试语句进行判断,再使用shell解释器内置的$?变量查看执行结果。因为/root/test为目录,因此返回0;test1.txt是文件,因此返回1.
逻辑语句用于对测试结果进行逻辑分析,根据测试结果可实现不一样的效果。逻辑运算符以下:
运算符 | 做用 |
---|---|
&& | 逻辑与,表示当前面的命令执行成功后才会执行后边的命令 |
|| | 逻辑或,表示当前面的命令执行失败后才会执行后边的命令 |
! | 逻辑非,表示把条件测试中判断结果取反。 |
案例:
判断当前登陆用户是否为管理员
[root@heimatengyun test]# [ ! $USER = root ] && echo "user" || echo "root"
root
复制代码
整数比较运算符仅是对数字的操做,不能将数字与字符串、文件等内容一块儿操做,并且不能想固然地使用平常生活中的等号、大于号、小于号等来进行判断。
ps:等号与赋值命令符冲突,大于小于号分别与输出输入重定向命令符冲突。所以必定要按照规范的整数比较运算符来进行操做。
可用的整数比较运算符以下表:
运算符 | 做用 |
---|---|
-eq | 是否等于 |
-ne | 是否不等于 |
-gt | 是否大于 |
-lt | 是否小于 |
-le | 是否小于或等于 |
-ge | 是否大于或等于 |
案例:
[root@heimatengyun test]# [ 1 -lt 2 ]
[root@heimatengyun test]# echo $?
0
[root@heimatengyun test]# [ 1 -gt 2 ]
[root@heimatengyun test]# echo $?
1
复制代码
字符串比较语句用于判断测试字符串是否为空或两个字符串是否相等。经常使用来判断某个变量是否未被定义,即内容为空值。字符串常见运算符以下表:
运算符 | 做用 |
---|---|
= | 比较字符串内容是否相同,相同为0 |
!= | 比较字符串内容是否不一样,不一样为0 |
-z | 判断字符串内容是否为空,空则为0 |
案例:
分别查看存在和不存在的变量,区别返回值
[root@heimatengyun test]# echo $USER
root
[root@heimatengyun test]# echo $TEST
[root@heimatengyun test]# [ -z $USER ]
[root@heimatengyun test]# echo $?
1
[root@heimatengyun test]# [ -z $TEST ]
[root@heimatengyun test]# echo $?
0
复制代码
至此,shell编程相关知识就介绍完毕,下一篇文章继续学习与用户和文件、权限相关的知识。