Bash是众多Linux发行版的默认shell程序,本教程将采用示例方式逐步讲解Bash脚本或者shell脚本,并可以自行编写简单的脚本程序。事实上,这些脚本通常被叫做shell脚本,但是由于Bash使用的最多,于是也可以称为Bash脚本。
Bash的基本概念就是通过编写一系列的Linux命令,还完成相应的工作。
为了通过一个步骤执行多条命令,可以通过使用分号 “;” 隔开不同命令,以达到顺序执行的目的。例如以下示例:
# pwd ; whoami
事实上,这已经可以认为是一个Bash脚本了。
首先执行pwd
命令显示当前目录,然后再执行whoami
命令显示当前用户。用分号间隔的两条命令表示第一条命令执行完再执行第二条命令。理论上可以通过分号将任意多的命令连接起来让系统顺序执行,但是条目还是有限制的,可以使用如下命令获得系统的最大数值:
# getconf ARG_MAX
于是就有了一个想法,我们为什么不把想要执行的命令存在一个文件里,这样既有利于脚本的保存,也避免了重复输入,这样Bash脚本文件就诞生了。
首先使用touch
命令创建一个文件,在脚本文件的开头,我们应定义于执行脚本的shell程序。这样的做的原因在于Linux世界里有众多的shell程序,而Bash只不过其中之一,如果不指定的话,脚本在执行时可能会报错。
Bash脚本的Shebang
脚本的第一行,要使用”#!”号来指定脚本使用的shell程序。”#!”的英文名称叫作Shebang或都Hashbang,在Unix和Linux世界中广泛使用。Bash脚本的第一行应该是如下样子:
#!/bin/bash
在之后的脚本文件中,如果以”#”号开头,则表示本行的内容不需要执行,一般用作注释。当然Shebang是个例外,因为它只是指定shell程序。
在Bash脚本中,每一行为一条命令,脚本会按顺序执行,例如之前的例子可以写成如下方式:
#!/bin/bash # This is a comment pwd whoami
当然,在Bash脚本同样可以一行执行多条命令,同样需要使用”;”号隔开。但是强烈建议脚本每行只使用一条命令,这样有利于未来脚本的维护。
设置脚本权限
编写完脚本后,将它保存到脚本文件中,就是我们第一个脚本文件。假设将以上的内容保存到一个名为”myscript”的文件中。
虽然文件中包含了Linux系统的命令,但是直接运行它的话依然会报错,提示文件不具备运行权限,所以要使用以下命令给文件设置可执行权限:
# chmod +x myscript # ./myscript
之后就可以执行这个脚本了,全部过程如图.1所示:
图.1 创建简单Bash脚本设置权限并执行
正式开始
由于之前的脚本只执行了命令,而对于不知道脚本内容的用户来说并不清楚脚本都做了什么。可以使用Linux系统的echo
命令来输出一些信息。将myscript脚本进行完善,修改成以下内容:
#!/bin/bash # our comment is here echo "The current directory is:" pwd echo "The user logged in is:" whoami
现在再看脚本的执行结果,就会发现要友好很多:
图.2 加入echo命令的Bash脚本
使用变量
变量是脚本中用来存储信息的特定标识,在Bash脚本中可以使用两种变量:
- 环境变量
- 用户变量
环境变量
当需要同系统进行交互时,就可以使用系统环境变量:
#!/bin/bash # display user home echo "Home for the current user is: $HOME"
注意脚本中$HOME是系统的环境变量,在双引号中仍然可以使用:
图.3 Bash脚本用系统环境变量
可以看到,脚本中使用$符号作为变更的前缀,用以指示其为变量。但是有多人马上会想到,如果想要使用$符号或者$符开头的内容的话,要如何处理呢。方法就是在$符前加入转义符”\”,这样就会把它们认为是普通字符而非变量。
图.4 Bash脚本中使用$符
比较两条命令的输出结果,就会发现如果不使用转义符,则Bash会认为”$one”是一个变量,由于其并没有定义,所有输出结果为空;而如果使用转义字符,则会正常输出相应内容。
用户变量
同样,在Bash中也可以使用用户自定义变量,使用方法是预先设定变量的值,然后通过使用$加以引用,示例如下:
#!/bin/bash # User variables grade=5 person="Adam" echo "$person is a good boy, he is in grade $grade"
图.5 Bash使用用户变量
命令代换
可以使用命令代换方式从命令的执行结果中提取信息,命令代换可以采用两种方式:
- 使用反向字符`(就是键盘1左边的那个字符)
- 使用$()格式
使用反向字符方式时注意不要同单引号 ‘ 相混淆,同时需要使用两个反向字符将命令包含起来:
# mydir=`pwd` OR # mydir=$(pwd)
脚本运行结果如图所示:
图.6 Bash使用反向字符
数学计算
脚本中可以采用”$((计算内容))”方式进行简单的数据计算,示例脚本如下:
#!/bin/bash var1=$(( 5 + 5 )) echo $var1 var2=$(( $var1 * 2 )) echo $var2
当然,这些数据计算都是比较简单的,复杂的数学运算最好不要在Bash脚本里尝试:
图.7 Bash脚本使用数学计算
使用if-then条件语句
如果Bash脚本中需要使用条件判断,比如某个数值等于真时做一些操作,而不等于时就不做,这就用到了条件语句,当然这个条件的内容是根据个人的需要来自行设置的。典型的if-then语句格式如下:
if command then do something fi
下面是一个示例:
#!/bin/bash if whoami then echo "It works" fi
其如果如图.8所示,可以看到,由于whoami
命令具有返回结果,故条件为真值,则会继续执行”then”语句中的内容。
图.8 Bash脚本if-then判断
当然,这个脚本比较简单,也没有什么实际用途。下面我们编写一个相对有用的脚本,可以判断系统中是否存在某个用户,脚本如下所示:
#!/bin/bash user=daehub if grep $user /etc/passwd then echo "User $user is exists" fi
由于系统中并不存在用户daehub,则执行脚本不会有任何输出,但是如果把”user”的值换成系统中存在的用户,比如root,那么脚本执行后会输出用户存在。执行步骤如下图所示:
图.9 Bash查找用户是否存在脚本
以上的条件显然不能完全满足需求,如果用户不存在,那么想要有操作的话,if-then语句就不能完全胜任了。于是就产生了 “if” 条件判断的增强版”if-else-then”。
if-else-then语句
标准的if-else-then语句使用如下结构:
if command then do something else do another thing fi
如果第一条命令执行后返回结果为0,则表示命令正确执行,那么就执行”do something”的内容;反之,就执行”eles”语句后面的内容,而不再执行前面的”do something”。示例脚本如下:
#!/bin/bash user=anotherUser if grep $user /etc/passwd then echo "The user $user Exists" else echo "The user $user doesn’t exist" fi
可以想象的到,脚本会根据用户名的不同,输出相应不同的结果,而不是像之前那样只对符合条件的结果输出。
图.10 Bash脚本使用if-else条件
现在已经做的不错了,当然我们还可以做的更好。如果我们需要很多”else”语句时,就需要采用如下方式:
if condition1 then commands elif condition2 then commands fi
如果条件1成立,那么就执行相应命令,如果不成立,而”elif”条件成立,就执行后面的命令,如果两个条件都不满足,那么就执行”fi”之后的命令。
结合测试
判断条件可以使用AND(&&)和OR(||)进行结合测试,其逻辑表示同其它语言类似,AND两侧的条件均为真是则整个表达式结果为真;OR两侧的条件有一条为真是则整个表达式结果为真。示例脚本如下:
#!/bin/bash dir=/home/rultr name="RULTR" if [ -d $dir ] && [ -n $name ]; then echo "The name exists and the folder $dir exists." else echo "One test failed" fi
bash 的内部命令中,中括号”[]”和test是等同的。如果我们不用绝对路径指明,通常我们用的都是bash自带的命令。if/test结构中的左中括号是调用test的命令标识,右中括号是关闭条件判断的。这个命令把它的参数作为比较表达式或者作为文件测试,并且根据比较的结果来返回一个退出状态码。if/test结构中并不是必须右中括号,但是新版的Bash中要求必须这样。
Test和[]中可用的比较运算符只有==和!=,两者都是用于字符串比较的,不可用于整数比较,整数比较只能使用-eq,-gt这种形式,下面就讲解如何进行数值比较。
数值比较
这里的数值比较单纯只整数值之间的比较,可以采用如下方式:
number1 -eq number2 //number1是否等于number2 number1 -ge number2 //number1是否大于等于number2 number1 -gt number2 //number1是否大于number2 number1 -le number2 //number1是否小于等于number2 number1 -lt number2 //number1是否小于number2 number1 -ne number2 //number1是否不等于number2
数值比较示例如下:
#!/bin/bash num=11 if [ $num -gt 10] //也可以写成if test $num -gt 10 then echo "$num is bigger than 10" else echo "$num is less than 10" fi
脚本的检测条件可以采用中括号方式,也可以直接使用test
命令,检测结果都是一样的。脚本执行结果如下图所示:
图.11 Bash脚本test检测
字符串比较
Bash中字符串使用如下方式进行比较:
string1 = string2 //string1是否等于string2 string1 != string2 //string1是否不等于string2 string1 < string2 //string1是否小于string2(首字符ASCII排序) string1 > string2 //string1是否大于string2(首字符ASCII排序) -n string1 //string1是否非空 -z string1 //string1是否为空
示例脚本如下所示:
#!/bin/bash user="RULTR root" if [ "$user" \< "$USER" ] then echo "The user $user is less than current logged in user $USER" fi
使用大于和小于号是需要注意,要在符号前加反斜杠”\”进行转义,以免出错。脚本执行如下图所示:
图.12 Bash脚本的字符串比较
可以看到,字符串比较并不是根据字符串长度进行比较,而是依据字符串首字符的ASCII数值大小进行比较,由于大写字母的ASCII值小于小写字母,所以字符串”RULTR root”是小于”root”的;同时,为了避免出错,需要将两个变量”$user”和”$USER”都用双引号括起,强制表示其为字符串,然后再进行比较。
文件比较
文件比较其实是利用test命令的各个参数,对文件进行相应检测,说明如下:
-d my_file //my_file是否为目录 -e my_file //my_file是否存在 -f my_file //my_file是否为文件 -r my_file //my_file是否可读 my_file –nt my_file2 //my_file是否新过my_file2 my_file –ot my_file2 //my_file是否旧过my_file2 -O my_file //my_file所有者是否为当前用户 -G my_file //my_file属组是否同登录用户相同
这些参数看起来比较复杂,不过用示例就很好说明。以”-d”为例,示意脚本如下:
#!/bin/bash mydir=/root if [ -d $mydir ] then echo "Directory $mydir exists" cd $mydir ls else echo "NO such file or directory $mydir" fi
首先检测目录是否存,如果存在就进行该目录并显示目录里的文件,如果不存在,则显示目录不存在。
图.13 Bash脚本文件比较
以上就是Bash的入门教程,根据本教程,可以对Bash脚本有一个循序渐进的了解。当然,这些内容还相对肤浅,要有更加深入的需求,就请参照相关手册作更细致的了解。