[toc]
阮一峰老师写的教程,这里做个精简笔记。
1 引号和转义
1.1 转义
$ & *
在 Bash 中有特殊含义,需要通过转义字符 \
进行转义后才能原样输出。
换行符也是一个特殊字符,表示命令的结束,Bash 收到这个字符以后,就会对输入的命令进行解释执行。
换行符前面加上反斜杠转义,就使得换行符变成一个普通字符,Bash 会将其当作空格处理,从而可以将一行命令写成多行。
1
2
3
4
5
6
$ mv \
/path/to/foo \
/path/to/bar
# 等同于
$ mv /path/to/foo /path/to/bar
1.2 引号
单引号用于保留字符的字面含义,各种特殊字符在单引号里面,都会变为普通字符,比如星号(*
)、美元符号($
)、反斜杠(\
)等。
双引号比单引号宽松,大部分特殊字符在双引号里都会失去特殊含义变成普通字符。但是,美元符号、反引号和反斜杠,这三个字符在双引号之中依然会被 Bash 自动扩展。
换行符在双引号中失去特殊含义,Bash 不再将其解释为命令的结束,只是作为普通的换行符。所以可以利用双引号,在命令行输入多行文本。
双引号还有一个作用,就是利用换行符失去特殊含义,可以保存原始命令的输出格式。
1
2
3
4
5
# 单行输出
$ echo $(cal)
# 原始格式输出
$ echo "$(cal)"
1.3 Here 文档/字符串
Here 文档(here document)是一种输入多行字符串的方法,格式如下。
1
2
3
<< token
text
token
它的格式分成开始标记(<< token
)和结束标记(token
)。开始标记是两个小于号 + Here 文档的名称,名称可以随意取,后面必须是一个换行符;结束标记是单独一行顶格写的 Here 文档名称,如果不是顶格,结束标记不起作用。两者之间就是多行字符串的内容。
1
2
3
4
5
6
7
8
9
10
11
12
13
$ cat << _EOF_
<html>
<head>
<title>
The title of your page
</title>
</head>
<body>
Your page content goes here.
</body>
</html>
_EOF_
Here 文档内部会发生变量替换,同时支持反斜杠转义,但是不支持通配符扩展,双引号和单引号也失去语法作用,变成了普通字符。
1
2
3
4
5
6
7
8
9
10
$ foo='hello world'
$ cat << _example_
$foo
"$foo"
'$foo'
_example_
hello world
"hello world"
'hello world'
如果不希望发生变量替换,可以把 Here 文档的开始标记放在单引号之中。
1
2
3
4
5
6
7
8
9
10
$ foo='hello world'
$ cat << '_example_'
$foo
"$foo"
'$foo'
_example_
$foo
"$foo"
'$foo'
Here 文档的本质是重定向,它将字符串重定向输出给某个命令,相当于包含了 echo
命令。
1
2
3
4
5
6
7
$ command << token
string
token
# 等同于
$ echo string | command
所以,Here 字符串只适合那些可以接受标准输入作为参数的命令,对于其他命令无效,比如echo
命令就不能用 Here 文档作为参数。
1
2
3
$ echo << _example_
hello
_example_
上面例子不会有任何输出,因为 Here 文档对于 echo
命令无效。
此外,Here 文档也不能作为变量的值,只能用于命令的参数。
Here 文档还有一个变体,叫做 Here 字符串(Here string),使用三个小于号(<<<
)表示。
1
<<< string
它的作用是将字符串通过标准输入,传递给命令。
有些命令直接接受给定的参数,与通过标准输入接受参数,结果是不一样的。所以才有了这个语法,使得将字符串通过标准输入传递给命令更方便,比如 cat
命令只接受标准输入传入的字符串。
2 变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
## 打印所有变量(自定义 + 环境)
$ set
## 打印环境变量
$ env
## 自定义变量,等号两边不能有空格
a=z # 变量 a 赋值为字符串 z
b="a string" # 变量值包含空格,就必须放在引号里面
c="a string and $b" # 变量值可以引用其他变量的值
d="\t\ta string\n" # 变量值可以使用转义字符
e=$(ls -l foo.txt) # 变量值可以是命令的执行结果
f=$((5 * 7)) # 变量值可以是数学运算的结果
## 读取变量,变量前加 $ 即可,如果变量值本身也是变量,可以使用 ${!varname} 的语法,读取最终值
$ myvar=USER
$ echo ${!myvar}
cy
## 删除变量
unset NAME
用户创建的变量仅可用于当前 Shell,子 Shell 默认读取不到父 Shell 定义的变量。为了把变量传递给子 Shell,需要使用 export
命令。这样输出的变量,对于子 Shell 来说就是环境变量。
2.1 特殊变量
$?,上一个命令的退出码,用于判断之前的命令是否执行成功。0 为成功,非 0 为失败
$$,当前 Shell 进程 id
$_,上一命令的最后一个参数
$!,最近一个后台执行的异步命令进程 id
1 2 3 4 5
$ firefox & [1] 11064 $ echo $! 11064
$0,当前 Shell 名称
$-,当前 Shell 启动参数
$@ $#,脚本参数数量
2.2 变量默认值
Bash 提供四个特殊语法,跟变量的默认值有关,目的是保证变量不为空。
1
2
3
4
5
6
7
8
9
10
11
## varname 存在且不为空,则返回它的值,否则返回 word。目的是返回一个默认值
${varname:-word}
## varname 存在且不为空,则返回它的值,否则将它设为 word,并且返回 word。目的是设置变量的默认值
${varname:=word}
## 变量名存在且不为空,则返回 word,否则返回空值。目的是测试变量是否存在
${varname:+word}
## varname 存在且不为空,则返回它的值,否则打印出 varname: message,并中断脚本的执行。如果省略了 message,则输出默认的信息 “parameter null or not set.”。目的是防止变量未定义
${varname:?message}
上面四种语法如果用在脚本中,变量名的部分可以用数字 1
到 9
,表示脚本的参数。
1
2
## 1 表示脚本的第一个参数。如果该参数不存在,就退出脚本并报错。
filename=${1:?"filename missing."}
3 字符串
1
2
3
4
5
6
7
## 字符串长度
${#varname}
## 子字符串,不能直接操作字符串,只能通过变量来读取字符串,并且不会改变原始字符串
${varname:offset:length}
## offset 为负数时,表示从末尾开始计算
搜索和替换,Bash 提供字符串搜索和替换的多种方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
## 字符串头匹配
# 如果 pattern 匹配变量 variable 的开头,
# 删除最短匹配(非贪婪匹配)的部分,返回剩余部分
${variable#pattern}
# 如果 pattern 匹配变量 variable 的开头,
# 删除最长匹配(贪婪匹配)的部分,返回剩余部分
${variable##pattern}
$ myPath=/home/cam/book/long.file.name
$ echo ${myPath#/*/}
cam/book/long.file.name
$ echo ${myPath##/*/}
long.file.name
$ path=/home/cam/book/long.file.name
$ echo ${path##*/}
long.file.name
$ phone="555-456-1414"
$ echo ${phone#*-}
456-1414
$ echo ${phone##*-}
1414
## 匹配不成功,返回原始字符串
$ phone="555-456-1414"
$ echo ${phone#444}
555-456-1414
## 字符串头替换
# 模式必须出现在字符串的开头
${variable/#pattern/string}
# 示例
$ foo=JPG.JPG
$ echo ${foo/#JPG/jpg}
jpg.JPG
## 字符串尾匹配
# 如果 pattern 匹配变量 variable 的结尾,
# 删除最短匹配(非贪婪匹配)的部分,返回剩余部分
${variable%pattern}
# 如果 pattern 匹配变量 variable 的结尾,
# 删除最长匹配(贪婪匹配)的部分,返回剩余部分
${variable%%pattern}
## 字符串尾替换
# 模式必须出现在字符串的结尾
${variable/%pattern/string}
# 示例
$ foo=JPG.JPG
$ echo ${foo/%JPG/jpg}
JPG.jpg
## 任意位置匹配
# 如果 pattern 匹配变量 variable 的一部分,
# 最长匹配(贪婪匹配)的那部分被 string 替换,但仅替换第一个匹配
${variable/pattern/string}
# 如果 pattern 匹配变量 variable 的一部分,
# 最长匹配(贪婪匹配)的那部分被 string 替换,所有匹配都替换
${variable//pattern/string}
$ path=/home/cam/foo/foo.name
$ echo ${path/foo/bar}
/home/cam/bar/foo.name
$ echo ${path//foo/bar}
/home/cam/bar/bar.name
# : 换成换行符
$ echo -e ${PATH//:/'\n'}
/usr/local/bin
/usr/bin
/bin
...
大小写
1
2
3
4
5
6
7
8
9
10
11
# 转为大写
${varname^^}
# 转为小写
${varname,,}
$ foo=heLLo
$ echo ${foo^^}
HELLO
$ echo ${foo,,}
hello
4 算术运算
((...))
语法可以进行整数的算术运算,这个语法不返回值,命令执行的结果根据算术运算的结果而定。只要算术结果不是 0
,命令就算执行成功。
1
2
3
4
5
6
7
8
9
10
$ (( 3 + 2 ))
$ echo $?
0
$ (( 3 - 3 ))
$ echo $?
1
$ echo $((2 + 2))
4
Bash 的数值默认都是十进制,但是在算术表达式中,也可以使用其他进制。
number
:没有任何特殊表示法的数字是十进制数(以10为底)。0number
:八进制数。0xnumber
:十六进制数。base#number
:base
进制的数。
1
2
3
4
$ echo $((0xff))
255
$ echo $((2#11111111))
255
$((...))
支持以下的二进制位运算符。
<<
:位左移运算,把一个数字的所有位向左移动指定的位。>>
:位右移运算,把一个数字的所有位向右移动指定的位。&
:位的“与”运算,对两个数字的所有位执行一个AND
操作。|
:位的“或”运算,对两个数字的所有位执行一个OR
操作。~
:位的“否”运算,对一个数字的所有位取反。^
:位的异或运算(exclusive or),对两个数字的所有位执行一个异或操作。
$((...))
支持以下的逻辑运算符。
<
:小于>
:大于<=
:小于或相等>=
:大于或相等==
:相等!=
:不相等&&
:逻辑与||
:逻辑或!
:逻辑否expr1?expr2:expr3
:三元条件运算符。若表达式expr1
的计算结果为非零值(算术真),则执行表达式expr2
,否则执行表达式expr3
。
如果逻辑表达式为真,返回1
,否则返回0
。
5 行操作
Bash 内置了 Readline 库,具有这个库提供的很多“行操作”功能,比如命令的自动补全,可以大大加快操作速度。
这个库默认采用 Emacs 快捷键,也可以改成 Vi 快捷键。
1
2
$ set -o vi
$ set -o emacs
光标移动
Ctrl + a
:移到行首Ctrl + e
:移到行尾Alt + b
:移动到当前单词的词首Alt + f
:移动到当前单词的词尾
Alt 键,可以用 ESC 键代替。
编辑操作
Ctrl + w
:删除光标前面的单词Ctrl + t
:光标位置的字符与它前面一位的字符交换位置(transpose)Alt + t
:光标位置的词与它前面一位的词交换位置(transpose)
自动补全
- Tab:完成自动补全
Alt + ?
:列出可能的补全,与连按两次 Tab 键作用相同Alt + /
:尝试文件路径补全
License:署名-相同方式共享 3.0