Bash脚本语法
很多时候都要求能使用 Shell 脚本进行编程,本文是根据阮一峰大神的 系列教程 总结的基本知识。
1. Shell 和 Bash
Shell 是一个程序,为用户提供和内核的交互界面,一般由用户从键盘输入命令,然后送给操作系统指向,执行完毕将结果返回给用户,因此又称为命令行环境。
只要能给用户提供命令行界面用于和内核交互,就叫 Shell,因此 Shell 有很多种,比如
- Bourne Shell(sh)
- Bourne Again shell(bash)
- C Shell(csh)
Bash 是目前最常用的 Shell,一般也是默认的 Shell。
具有图形界面的 Linux 打开的命令行一般是终端模拟器,顾名思义,就是模拟命令行环境的,不是真正的命令行,但使用和直接使用非图形界面的命令行没有区别。因此我们随后不区分终端和命令行,全部使用命令行这个称呼。
图形界面下使用 Ctrl + Alt + T
打开命令行,默认位于用户主目录 ~
,提示符为美元符号 $
,输入 exit
命令或使用快捷键 Ctrl + D
退出当前命令行。
2. 预备知识
使用 echo
命令可以在屏幕上输入文本,添加引号可以输出多行文本,添加 -e
参数会解释引号中的特殊字符。该命令经常使用。
|
|
Bash 单个命令一般都是一行,按下回车开始执行,比较长的命令可以写成多行,只需要在每一行的末尾加上反斜杠即可
|
|
&&
和 ||
用于连接两个命令,前者的含义是如果第一个命令执行成功,执行第二个命令;后者的含义是如果第一个命令执行失败,执行第二个命令,第一个命令执行成功就不会执行第二个命令了。
|
|
Bash 只有一种数据类型,就是字符串。不管用户输入什么数据,Bash 都视为字符串。
还有一些实用的快捷键
Ctrl + L
:清除屏幕并将当前行移到页面顶部。Ctrl + C
:中止当前正在执行的命令。Shift + PageUp
:向上滚动。Shift + PageDown
:向下滚动。Ctrl + U
:从光标位置删除到行首。Ctrl + K
:从光标位置删除到行尾。↑
,↓
:浏览已执行命令的历史记录。Tab
:补全未完全输入的命令或路径
3. 模式扩展
用户输入的命令中有时包括一些特殊的字符,Shell 会先对这些字符进行替换,然后才开始执行,这个过程叫做模式扩展。Shell 提供 8 种模式扩展。
~
:用户主目录?
:单个字符,但不包括空字符*
:任意数量的任意字符,包括 0 个[...]
:匹配方括号中出现的任意字符,如果前面加了^
或!
表示匹配方括号中没有出现的字符1 2 3 4 5 6 7
# 只存在文件 a.txt $ ls [ab].txt a.txt # 存在 aaa、bbb、aba 三个文件 $ ls ?[!a]? aba bbb
[start-end]
:匹配一个连续的范围,同样,实用!
可以匹配不属于该范围的字符1 2 3 4 5
# 存在文件 a.txt、b.txt 和 c.txt $ ls [a-c].txt a.txt b.txt c.txt
{...}
:匹配大括号中的所有字符1 2
$ echo d{a,e,i,u,o}g dag deg dig dug dog
{start...end}
:扩展成一个连续序列1 2
$ echo {1..4} 1 2 3 4
$
:将$
开头的变量替换为变量的值1 2
$ echo $SHELL /bin/bash
$(...)
:扩展成另一个命令的运行结果,该命令所有输出都作为返回值1 2
$ echo $(date) Tue Jan 28 00:01:13 CST 2020
$((...))
:扩展成整数运算的结果1 2
$ echo $((2 + 2)) 4
模式扩展先于正则表达式出现,可以看作原始的正则表达式,但没有正则表达式灵活。
4. 变量
用env
命令显示所有环境变量,echo
命令查看单个环境变量的值
|
|
BASH 的变量名区分大小写,由于环境变量一般使用全大写,用户自定义变量一般也使用全大写。
4.1 创建变量
变量声明语法如下,等号两边不可有空格
|
|
如果变量的值中有空格,使用引号包围
|
|
BASH 没有数据类型的概念,所有变量都是字符串。
下面是一些自定义变量的例子。
|
|
变量可以重复赋值,后面的赋值会覆盖前面的赋值。
|
|
上面例子中,变量foo
的第二次赋值会覆盖第一次赋值。
4.2 读取变量
读取变量的时候,直接在变量名前加上$
就可以了。
|
|
如果变量不存在,Bash 不会报错,而会输出空字符。
读取变量的时候,变量名也可以使用花括号{}
包围,比如$a
也可以写成${a}
。这种写法可以用于变量名与其他字符连用的情况。
|
|
4.3 删除变量
unset
命令用来删除一个变量。
|
|
这个命令不是很有用。因为不存在的 Bash 变量一律等于空字符串,所以即使unset
命令删除了变量,还是可以读取这个变量,值为空字符串。
所以,删除一个变量,也可以将这个变量设成空字符串。
|
|
4.4 输出变量
用户创建的变量仅可用于当前 Shell,子 Shell 默认读取不到父 Shell 定义的变量。为了把变量传递给子 Shell,需要使用export
命令。这样输出的变量,对于子 Shell 来说就是环境变量。
export
命令用来向子 Shell 输出变量。
|
|
上面命令输出了变量NAME
。变量的赋值和输出也可以在一个步骤中完成。
|
|
上面命令执行后,当前 Shell 及随后新建的子 Shell,都可以读取变量$NAME
。
子 Shell 如果修改继承的变量,不会影响父 Shell。
|
|
5. 脚本
脚本(script)就是包含一系列命令的一个文本文件。Shell 读取这个文件,依次执行里面的所有命令,就好像这些命令直接输入到命令行一样。所有能够在命令行完成的任务,都能够用脚本完成。
Bash 脚本使用 #
声明注释
5.1 Shebang行
脚本的第一行通常是指定解释器,即这个脚本必须通过什么解释器执行。这一行以#!
字符开头,这个字符称为 Shebang,所以这一行就叫做 Shebang 行。
#!
后面就是脚本解释器的位置,Bash 脚本的解释器一般是/bin/sh
或/bin/bash
。
|
|
5.2 执行权限和路径
指定了 Shebang 行的脚本,需要赋予执行权限才可以直接执行。
权限的设定有两种格式,一种是字母格式,(r, w, x)分别表示读、写、执行,(u, g, o)分别表示脚本所有者、同组用户、其它用户,(+, -, =)分别表示增加权限、取消权限和设置唯一权限。所有的权限设定命令都是这些参数的组合,举例
|
|
权限还可以以数字方式设定,我们令 r=4, w=2, x=1,权限的组合就是这些操作权重的组合,权限授予的用户就是数字的位置,举例
|
|
5.3 脚本参数
调用脚本的时候,脚本文件名后面可以带有参数。
|
|
脚本文件内部,可以使用特殊变量,引用这些参数。
$0
:脚本文件名,即script.sh
。$1
~$9
:对应脚本的第一个参数到第九个参数。$#
:参数的总数。$@
:全部的参数,参数之间使用空格分隔。$*
:全部的参数,参数之间使用变量$IFS
值的第一个字符分隔,默认为空格,但是可以自定义。
如果脚本的参数多于9个,那么第10个参数可以用${10}
的形式引用,以此类推。
用户可以输入任意数量的参数,利用for
循环,可以读取每一个参数。
|
|
注1,如果命令是command -o foo bar
,那么-o
是$1
,foo
是$2
,bar
是$3
。
注2,如果命令是./script.sh "a b"
,即多个参数放在双引号里面,视为一个参数。
5.4 命令执行
exit
命令用于终止当前脚本的执行,并向 Shell 返回一个退出值。
|
|
上面命令中止当前脚本,将最后一条命令的退出状态,作为整个脚本的退出状态。
exit
命令后面可以跟参数,该参数就是退出状态。
|
|
可以使用 $?
判断上一条命令的执行结果控制脚本的执行
|
|
使用 if
命令可以直接判断
|
|
最简便的方法是使用逻辑连接符
|
|
source
命令用于执行一个脚本,通常用于重新加载一个配置文件。
|
|
source
命令最大的特点是在当前 Shell 执行脚本,不像直接执行脚本时,会新建一个子 Shell。所以,source
命令执行脚本时,不需要export
变量。
|
|
source
有一个简写形式,可以使用一个点(.
)来表示。
|
|
注意,要区分 . script.sh
和 ./script.sh
5.5 别名
alias
命令用来为一个命令指定别名,这样更便于记忆。下面是alias
的格式。
|
|
上面命令中,NAME
是别名的名称,DEFINITION
是别名对应的原始命令。注意,等号两侧不能有空格,否则会报错。
一个常见的例子是为grep
命令起一个search
的别名。
|
|
alias
也可以用来为长命令指定一个更短的别名。下面是通过别名定义一个today
的命令。
|
|
有时为了防止误删除文件,可以指定rm
命令的别名。
|
|
上面命令指定rm
命令是rm -i
,每次删除文件之前,都会让用户确认。
6. 用户输入
有时,脚本需要在执行过程中,由用户提供一部分数据,这时可以使用read
命令。它将用户的输入存入一个变量,方便后面的代码使用。用户按下回车键,就表示输入结束。
read
命令的格式如下。
|
|
上面语法中,options
是参数选项,variable
是用来保存输入数值的一个或多个变量名。如果没有提供变量名,环境变量REPLY
会包含用户输入的一整行数据。
下面是一个例子demo.sh
。
|
|
上面例子中,先显示一行提示文本,然后会等待用户输入文本。用户输入的文本,存入变量text
,在下一行显示出来。
|
|
read
可以接受用户输入的多个值。
|
|
上面例子中,read
根据用户的输入,同时为两个变量赋值。
如果用户的输入项少于read
命令给出的变量数目,那么额外的变量值为空。如果用户的输入项多于定义的变量,那么多余的输入项会包含到最后一个变量中。
如果read
命令之后没有定义变量名,那么环境变量REPLY
会包含所有的输入。
|
|
上面脚本的运行结果如下。
|
|