我的GitHub | 我的博客 | 我的微信 | 我的邮箱 |
---|---|---|---|
baiqiantao | baiqiantao | bqt20094 | baiqiantao@sina.com |
目录
Bash 的模式扩展
本文改编自 网道的 Bash 教程,主要为了精简大量本人不感兴趣的内容。
简介
Shell 接收到用户输入的命令以后,会根据空格将用户的输入,拆分成一个个词元(token
)。然后,Shell 会扩展词元里面的特殊字符(~
、?
、*
、[]
、{}
、$
),扩展完成后才会调用相应的命令。这种特殊字符的扩展,称为模式扩展(globbing
)。
Bash 是先进行扩展,再执行命令。扩展的结果是由 Bash 负责的,与所要执行的命令无关。命令本身并不存在参数扩展,收到什么参数就原样执行。
模块扩展的英文单词是
globbing
,这个词来自于早期的 Unix 系统有一个/etc/glob
文件,保存扩展的模板。后来 Bash 内置了这个功能,但是这个名字就保留了下来。
模式扩展与正则表达式的关系是:模式扩展的出现早于正则表达式,可以看作是原始的、早期的、低配版正则表达式,它的功能没有正则那么强大灵活,但是优点是简单和方便。
set -f # 【set -o noglob】关闭扩展
set +f # 【set +o noglob】打开扩展
文件名扩展
文件名扩展字符只有 文件存在 的前提下才会扩展,如果不存在可匹配的文件名,就会原样输出。
用户主目录 ~
波浪线~
会扩展成当前用户的主目录。
echo ~ # 【/home/bqt】波浪线 ~ 会扩展成当前用户的主目录
echo ~/foo # 【/home/bqt/foo】主目录的某个子目录
echo ~root # 【/root】用户 root 的主目录
echo ~foo # 【~foo】如果用户名 foo 不存在,就会原样输出
echo ~+ # 扩展成当前所在的目录,等同于【pwd】命令
单个字符 ?
字符?
代表文件路径里面的任意单个字符,不包括空字符。
ls ?.txt # 【a.txt】字符 ? 代表文件路径里面的任意单个字符,不包括空字符
ls ??.txt # 【ab.txt】如果匹配多个字符,就需要多个 ? 连用
echo ?.bqt # 【?.bqt】如果不存在可匹配的文件名,就会原样输出
任意字符 *
字符*
代表文件路径里面的任意数量的任意字符,包括零个字符。*
不会匹配以.
开头的隐藏文件
echo *.txt # 【a.txt ab.txt】字符 * 代表文件路径里面的任意数量的任意字符,包括零个字符
echo * # 输出当前目录所有文件,不包括以 . 开头的隐藏文件,不包括子目录中的文件
echo *.bqt # 【*.bqt】如果不存在可匹配的文件名,就会原样输出
echo .* # 输出当前目录所有隐藏文件(即以 . 开头的文件)
echo .[!.]* # 输出当前目录所有隐藏文件,同时排除 .(当前目录)和 ..(上级目录)这两个隐藏文件
*
只匹配当前目录,不会匹配子目录
echo */ # 输出当前目录所有子目录
echo */* # 输出当前目录所有子目录中的所有文件
echo */*/ # 输出当前目录所有二级子目录
echo */*/* # 输出当前目录所有二级子目录中的所有文件,有几层子目录,就必须写几层星号
打开 globstar
参数以后,**
可以匹配零个或多个任意深度子目录(Bash 4.0 中新增)
shopt globstar # 查询某个参数关闭还是打开,globstar 参数默认是关闭的
shopt -s globstar # 打开某个参数,打开 globstar 参数以后,** 可以匹配零个或多个子目录
shopt -u globstar # 关闭某个参数
echo **/* # 输出当前目录以及【任意深度子目录】中的所有文件
任一字符 []
- 方括号扩展
[...]
可以扩展任意一个括号之中的字符 - 排除模式
[^...]
和[!...]
可以扩展任意一个不在方括号中的字符 - 如果需要匹配字符
[
,可以放在方括号内任意位置,不支持匹配字符]
- 如果需要匹配字符
-
,只能放在方括号内部的开头或结尾
echo [ab].txt # 方括号扩展 [...] 可以扩展任意一个括号之中的字符
echo [^ab] # 等价于 [!ab],表示扩展任意一个不在方括号中的字符
echo [[ab] # 如果需要匹配字符 [,可以放在方括号内任意位置,不支持匹配字符 ]
echo [-ab] # 如果需要匹配字符 -,只能放在方括号内部的开头或结尾
echo [ab].bqt # 【[ab].bqt】如果不存在可匹配的文件名,就会原样输出
方括号扩展有一个简写形式[start-end]
,表示匹配一个连续的范围。
[a-z]
:所有小写字母的字符[!a-z]
:所有非小写字母的字符[a-zA-Z]
:所有小写字母 + 大写字母[a-zA-Z0-9]
:所有小写字母 + 大写字母 + 数字program.[co]
:文件program.c
与文件program.o
BACKUP.[0-9][0-9][0-9]
:所有以BACKUP.
开头,后面是三个数字的文件名
字符类 [[:class:]]
[[:class:]]
表示一个字符类,扩展成某一类特定字符之中的一个。
[[:digit:]]
:匹配任意数字 0-9[[:lower:]]
:匹配任意小写字母 a-z[[:upper:]]
:匹配任意大写字母 A-Z[[:alpha:]]
:匹配任意英文字母[[:alnum:]]
:匹配任意英文字母与数字[[:cntrl:]]
:ASCII 码 0-31 的不可打印字符[[:print:]]
:ASCII 码 32-127 的可打印字符[[:punct:]]
:标点符号(除了 A-Z、a-z、0-9 的可打印字符)[[:graph:]]
:匹配任意英文字母、数字、标点符号[[:blank:]]
:空格和 Tab 键[[:space:]]
:空格、Tab、LF(10)、VT(11)、FF(12)、CR(13)[[:xdigit:]]
:匹配16进制字符(A-F、a-f、0-9)
echo [[:upper:]]* # 输出所有以大写字母开头的文件名
echo [![:digit:]]* # 输出所有不以数字开头的文件名
echo [[:blank:]]* # 【[[:blank:]]*】如果不存在可匹配的文件名,就会原样输出
量词语法 ?+*!@()
量词语法用来控制模式匹配的次数。它只有在 Bash 的 extglob
参数打开的情况下才能使用,不过一般是默认打开的。
shopt extglob # 查询某个参数关闭还是打开,extglob 参数默认是打开的
shopt -s extglob # 打开某个参数,打开 extglob 参数以后,可以使用量词语法
shopt -u extglob # 关闭某个参数
?(pattern-list)
:匹配零个或一个模式*(pattern-list)
:匹配零个或多个模式+(pattern-list)
:匹配一个或多个模式@(pattern-list)
:只匹配一个模式!(pattern-list)
:匹配给定模式以外的任何内容
ls abc?(def) # 【abc abcdef】匹配以 abc 开头、且以零个或一个 def 结尾的文件
ls abc+(.txt|.php) # 【abc.php abc.txt】匹配以 abc 开头、且以 .txt 或 .php 结尾的文件
ls abc+(.txt) # 【abc.txt abc.txt.txt】匹配以 abc 开头、且以 .txt 结尾的文件
ls a!(b)c.txt # 【aac.txt ac.txt】匹配除了 abc.txt 以外,以 a 开头、且以 c.txt 结尾的文件
echo abc*(.bqt) # 【abc*(.bqt)】如果不存在可匹配的文件名,就会原样输出
字符串扩展
所有值 {}
大括号扩展 {...}
表示分别扩展成大括号里面的所有值。
- 各个值之间使用逗号分隔,值可以是多字符的模式
- 逗号前面可以没有值,表示扩展的第一项为空
- 逗号前后不能有空格,否则,Bash 会认为这不是大括号扩展,而是独立的参数
- 大括号可以嵌套,可以与其他模式联用,并且总是先于其他模式进行扩展
- 大括号扩展不是文件名扩展,它总是会扩展的,这与方括号扩展
[...]
完全不同
echo {1,2,3} # 【1 2 3】大括号扩展 {...} 表示分别扩展成大括号里面的所有值
ls {,a,abc}.txt # 依次访问 .txt a.txt abc.txt 三个文件,而不管文件是否存在
echo a{a{1,2},b{3,4}} # 【aa1 aa2 ab3 ab4】支持嵌套
echo /bin/{cat,ba*} # 大括号也可以与其他模式联用,并且总是先于其他模式进行扩展
大括号扩展可直接用于for
循环。
for i in {1,2,4,白乾涛}
do
echo $i
done
- 大括号扩展的简写形式
{start..end}
表示扩展成一个连续序列 - 大括号扩展另一个简写形式
start..end..step
可指定扩展的步长 - 简写形式支持逆序、支持嵌套
echo {01..5} # 【01 02 03 04 05】扩展成一个连续序列
echo {0..8..2} # 【0 2 4 6 8】可指定扩展的步长
echo {a..c}{1,2} # 【a1 a2 b1 b2 c1 c2】多个简写形式连用,会有循环处理的效果
echo .{mp{5..3},mkv} # 【.mp5 .mp4 .mp3 .mkv】支持逆序,支持嵌套
echo {a1..3c} # 【{a1..3c}】遇到无法理解的简写时会原样输出,不会扩展
美元符号扩展
变量扩展 $
Bash 将美元符号$
开头的词元视为变量,将其扩展成变量值。${!string*}
或${!string@}
返回所有匹配给定字符串string
的变量名。
echo $SHELL # 【/bin/bash】
echo ${SHELL} # 【/bin/bash】变量名也可以放在 ${} 里面
echo ${!S*} # 返回所有以 S 开头的变量名,如果不存在,则返回空
子命令扩展 $(...)
$(...)
可以扩展成另一个命令的运行结果,该命令的所有输出都会作为返回值。
echo $(date) # 返回 date 命令的运行结果,注意 date 是一个命令而非一个变量
echo `date` # 将子命令放在反引号之中,也可以扩展成命令的运行结果
echo $(ls $(pwd)) # 子命令扩展可以嵌套
算术扩展 $((...))
$((...))
可以扩展成整数运算的结果。
echo $((2 + 2)) # 4
模式扩展相关的 shopt 命令
shopt
命令可以调整 Bash 的行为。它有好几个参数跟通配符扩展有关。
shopt [optionname] # 查询某个参数关闭还是打开
shopt -s [optionname] # 打开某个参数
shopt -u [optionname] # 关闭某个参数
- dotglob:默认关闭,可以让扩展结果包括隐藏文件,即以点开头的文件。
- nullglob:默认关闭,可以让通配符不匹配任何文件名时,返回空字符串而非
原样返回
。 - failglob:默认关闭,可以让通配符不匹配任何文件名时,直接报错,而非继续让各个命令去处理。
- extglob:默认打开,可以让 Bash 支持 ksh 的一些扩展语法。它的主要应用是支持
量词语法
。 - nocaseglob:默认关闭,可以让通配符扩展不区分大小写。
- globstar:默认关闭,可以让
**
匹配零个或多个子目录(Bash 4.0 中新增)。
模式扩展使用注意点
- 通配符是先解释,再执行:Bash 接收到命令以后,如果发现里面有通配符,会先进行通配符扩展,然后再执行命令。
- 文件名扩展在没有可匹配的文件时,会原样输出。
- 所有文件名扩展只匹配单层路径,无法匹配子目录里面的文件(即:不能匹配路径分隔符
/
)。 - Bash 允许文件名使用通配符,即文件名包括特殊字符。这时引用文件名,需要把文件名放在
单引号或双引号
里面。
2022-1-2