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脚本有一个循序渐进的了解。当然,这些内容还相对肤浅,要有更加深入的需求,就请参照相关手册作更细致的了解。

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注