Bash 7 模式拓展
Table of Contents
1 模式扩展 globbing
Shell 收到用户指令后,依据空格,将输入拆分成一个个的词元 (token)。然后 Shell 会根据词元中所含的特殊字符的语义,将这些特殊字符扩写为普通字符,形成最终命令。最后执行最终命令。
这种特殊字符的扩展,称为模式扩展 (globbing)。其中有些用到通配符,又称为通配符扩展 (wildcard expansion)。
Bash 先进行 token 扩展,再执行命令。因此,扩展的结果是由 Bash 负责的,与所要执行的命令无关。命令本身并不存在参数扩展,收到什么参数就原样执行。
模式扩展早于正则表达式出现,可以看作是原始的正则表达式。所以,模式扩展的功能虽没有正则表达式强大灵活,但胜在简单方便。
Bash 允许用户关闭扩展:
$ set -o noglob # 或者 $ set -f
打开扩展:
$ set +o noglob # 或者 $ set +f
Bash 一共提供 8 种扩展:
~
字符扩展。?
字符扩展。*
字符扩展。[]
扩展。{}
扩展。- 变量扩展。
- 子命令扩展。
- 算术扩展。
- 字符类扩展。
?
*
扩展属于路径扩展,只有路径存在的前提下,才会发生扩展。如果文件不存在,扩展就不会发生。
2 ~ 字符扩展
2.1 ~ 与 ~/dir
~
会自动扩展成当前用户的主目录:
$ echo ~ /home/lsz $ echo ~/foo /home/lsz/foo
2.2 ~user
~user
表示根据用户 user
的名称,返回该用户的主目录:
$ echo ~foo /home/foo $ echo ~root /root
如果 ~user
的 user
是不存在的用户名,则波浪号扩展不起作用:
$ echo ~nonExistedUser ~nonExistedUser
2.3 ~+
~+
会扩展成当前所在的目录,等同于 pwd
命令。
3 ? 字符扩展
?
表示路径中的单个字符,不包括空字符。
# 存在文件 a.txt 和 b.txt $ ls ?.txt a.txt b.txt
多个 ?
连用以匹配多个字符:
# 存在文件 a.txt b.txt 和 ab.txt $ ls ??.txt ab.txt # 当前目录文件夹结构: # ./ # ├── aa # │ └── aa_file.txt # ├── ab # │ └── ab_file.txt # └── ac $ ls ./a? ./aa: aa_file.txt ./ab: ab_file.txt ./ac:
4 星号 * 字符扩展
*
代表路径中 0 个或多个字符,但不匹配隐藏文件 (以 .
开头的文件)。
# 存在文件 a.txt b.txt 和 ab.txt $ ls a*.txt a.txt ab.txt $ ls *b* b.txt ab.txt
如果要匹配隐藏文件,需要写成 .*
。如果要匹配隐藏文件,同时要排除 .
和 ..
这两个特殊的隐藏文件,可以与方括号扩展结合使用,写成 .[!.]*
。
$ echo .* $ echo .[!.]*
要匹配子目录中的内容,就要在路径上写 *
匹配,有几层子目录,就必须写几层星号:
# 当前目录下的某个子目录内有一个 a.txt # 无效的写法 $ ls *.txt # 有效的写法 $ ls */*.txt
Bash 4.0 引入了一个参数 globstar
,当该参数打开,允许用 **
来匹配零个或多个子目录。因此, **/*.txt
可以匹配顶层目录和任意深度子目录内的 *.txt
文件。详细请看 shopt
命令的笔记。
5 [] 扩展
5.1 基本形式
[]
中的任一字符出现一次:
# 存在文件 a.txt、b.txt $ ls [ab].txt a.txt b.txt # 只有 a.txt $ ls [ab].txt a.txt
如果要匹配 [
字符,可以放在方括号内,比如 [[aeiou]
。
如果要匹配连字号 -
,只能放在 []
内的开头或结尾,比如 [-aeiou]
或 [aeiou-]
。
5.2 [^...] 和 [!...]
[^...]
和 [!...]
两种变体等价,表示匹配不在 []
里面的字符。比如 [^abc]
或 [!abc]
表示匹配除 a
或 b
或 c
以外的字符。
# 存在 aaa aba bbb 三个文件 $ ls ?[!a]? aba bbb
5.3 [start-end] 形式
[start-end]
表示匹配一个连续的范围。比如 [a-c]
等同于 [abc]
, [0-9]
匹配 [0123456789]
。
# 存在文件 a.txt、b.txt 和 c.txt $ ls [a-c].txt a.txt b.txt c.txt # 存在文件 report1.txt、report2.txt 和 report3.txt $ ls report[0-9].txt report1.txt report2.txt report3.txt
常用简写:
[0-9]
: 所有数字。[a-zA-Z]
: 所有字母。[A-Z]
: 所有大写字母。[a-z]
: 所有小写字母。[a-zA-Z0-9]
: 所有字母与数字。program.[co]
: 文件program.c
与文件program.o
。
[!start-end]
表示匹配不属于这个范围的字符。比如 [!a-zA-Z]
匹配非英文字母的字符。
# 存在文件 report1.txt、report2.txt 和 report3.txt $ echo report[!1–3].txt report4.txt report5.txt
6 {} 扩展
6.1 基本形式
{}
内各个值之间使用 ,
分隔,且逗号前后不能有空格,表示分别用 {}
内的各个值替换表达式整体。比如 a{1,2,3}
扩展成 a1
a2
a3
。
$ echo {1,2,3} 1 2 3 $ echo d{a,e,i,u,o}g dag deg dig dug dog
逗号 , 前面可以没有值,表示插入一个空的扩展项:
$ echo a.log{,.bak} a.log a.log.bak
大括号 {} 表达式可以嵌套:
$ echo {j{p,pe}g,png} jpg jpeg png
可与其他模式联用,并总优先于其他扩展:
$ echo /bin/{cat,b*} /bin/cat /bin/b2sum /bin/base32 /bin/base64 ... ...
大括号 {} 表达式可以用于多字符模式匹配,中括号 [] 表达式只能匹配单字符。
$ echo {pig,dog} pig dog
不论结果如何,大括号 {} 表达式扩展都会发生。
$ ls {a,b,c}.txt ls: cannot access 'a.txt': No such file or directory ls: cannot access 'b.txt': No such file or directory ls: cannot access 'c.txt': No such file or directory
6.2 {start..end} 形式
6.2.1 基本用法
{start..end}
表示扩展成一个连续序列。比如, {a..z}
可以扩展成所有的小写英文字母:
$ echo {a..c} a b c $ echo d{a..d}g dag dbg dcg ddg
这种形式支持逆序:
$ echo {c..a} c b a $ echo {5..1} 5 4 3 2 1
如果整数前面有前导 0,扩展输出的每一项都有前导 0。
$ echo {01..5} 01 02 03 04 05 $ echo {001..5} 001 002 003 004 005
遇到无法理解的形式,原样输出:
$ echo {a1..3c} {a1..3c}
多个简写形式连用,会有循环处理的效果。
$ echo {a..c}{1..3} a1 a2 a3 b1 b2 b3 c1 c2 c3
可以嵌套使用,形成复杂的扩展:
$ echo .{mp{3..4},m4{a,b,p,v}} .mp3 .mp4 .m4a .m4b .m4p .m4v
6.2.2 常见用法
新建一系列目录:
$ mkdir {2007..2009}-{01..12}
另一个常见用途,是直接用于for循环:
for i in {1..4}
do
echo $i
done
6.3 {start..end..step} 形式
{start..end..step}
可以指定扩展的步长:
$ echo {0..10..2} 0 2 4 6 8 10
7 变量扩展
Bash 将 $
开头的词元视为变量,将其扩展成变量值。
$ echo $SHELL /bin/bash
变量名也可以放在 ${}
里面。
$ echo ${SHELL} /bin/bash
${!string*}
或 ${!string@}
返回所有匹配给定字符串 string
的变量名。
# 扩展成所有以S开头的变量名 $ echo ${!S*} SECONDS SHELL SHELLOPTS SHLVL SSH_AGENT_PID SSH_AUTH_SOCK
8 子命令扩展
$(...)
可以扩展成另一个命令的运行结果,该命令的所有输出都会作为返回值。
$ echo $(date) Tue Jan 28 00:01:13 CST 2020
还有另一种较老的语法,子命令放在反引号之中,也可以扩展成命令的运行结果。
$ echo `date` Tue Jan 28 00:01:13 CST 2020
$(...)
可以嵌套,比如 $(ls $(pwd))
。
9 算术扩展
$((...))
可以扩展成整数运算的结果。
$ echo $((2 + 2)) 4
10 字符类扩展
[[:class:]]
表示一个字符类,扩展成某一类特定字符之中的一个:
[[:alnum:]]
: 匹配任意英文字母与数字[[:alpha:]]
: 匹配任意英文字母[[:blank:]]
: 空格和 Tab 键。[[:cntrl:]]
: ASCII 码 0-31 的不可打印字符。[[:digit:]]
: 匹配任意数字 0-9。[[:graph:]]
: A-Z、a-z、0-9 和标点符号。[[:lower:]]
: 匹配任意小写字母 a-z。[[:print:]]
: ASCII 码 32-127 的可打印字符。[[:punct:]]
: 标点符号(除了 A-Z、a-z、0-9 的可打印字符)。[[:space:]]
: 空格、Tab、LF(10)、VT(11)、FF(12)、CR(13)。[[:upper:]]
: 匹配任意大写字母 A-Z。[[:xdigit:]]
: 16进制字符(A-F、a-f、0-9)。
11 各扩展的匹配次数
量词语法可以用来控制模式匹配的次数。它只有在 Bash 的 extglob
参数打开的情况下生效。下面的命令可以查询:
$ shopt extglob extglob on
如果extglob参数是关闭的,可以用下面的命令打开。
$ shopt -s extglob
量词语法有下面几个。
?(pattern-list)
: 匹配 0 个或 1 个模式。*(pattern-list)
: 匹配 0 个或多个模式。+(pattern-list)
: 匹配 1 个或多个模式。@(pattern-list)
: 匹配 1 个模式。!(pattern-list)
: 匹配给定模式以外的任何内容。
# ?(def) 匹配零个或一个 def $ ls abc?(def) abc abcdef
# 匹配一个 .txt 或 .php $ ls abc+(.txt|.php) abc.php abc.txt