Shell 学习(3)Bash 的模式扩展

模式扩展

Shell 接受到用户输入的命令后,会根据输入内容通过单个空格进行分割,拆分成一个个词元(Token)。然后 Shell 会对词元中的期待的特殊字符进行扩展,扩展后在调用相应的命令。这样的特殊字符的扩展称之为模式扩展(globbing)。有些用到通配符的地方,称之为通配符扩展(wildcard expansion)。

再着重明一下:扩展是由 Bash 完成的,不是执行的命令扩展的。当 Bash 扩展完后在调用执行的命令,命令接收到什么参数就使用什么参数(参数可能是已经被扩展过的)。

可以发现,模式扩展 与 正则表达式的部分内容十分相似,二者的关系是什么样的?实际上,模式扩展更早与正则,但是功能没有正则表达式那么强大。优点是简单、方便,可以看为是最原始的正则。

Bash 一共提供八种扩展:

  1. 波浪线扩展
  2. ? 字符扩展
  3. * 字符扩展
  4. 方括号扩展
  5. 大括号扩展
  6. 变量扩展
  7. 子命令扩展
  8. 算术扩展

波浪线扩展

~:扩展为当前登录用户的目录。

$ echo ~ 
/root

$ echo ~root
/root

$ echo ~+
/home/shellTest

第一个命令输出当前登录用户的目录路径,因为当前登录用户为root,所以第一个命令输出为 root 用户的目录路径。

第二个命令中的 ~root 这样的格式,会被扩展为:指定用户的目录路径。结合第一条命令可以说明,~ 后不追加用户名,则默认为当前登录用户。

第三个命令中的 ~+ 会被扩展为当前用户所在目录,即同 pwd

? 字符扩展

? 扩展为匹配任意单个字符

$ ls
test.sh

$ ls ?.sh
ls: cannot access ?.sh: No such file or directory

$ ls ????.sh
test.sh

* 字符扩展

* 扩展为匹配任意数量的任意字符(包含零个字符)

$ ls
ab.txt  a.txt  b.txt

$ ls *
ab.txt  a.txt  b.txt

$ ls a*.txt
ab.txt  a.txt

ls * 无法查看隐藏文件( 文件名为.开头的文件,即 touch .hidden.txt

如果需要查看隐藏文件,可通过:echo .*

$ touch .hidden.txt

$ echo .*
. .. .hidden.txt

其中 . .. 就是当前目录、当前目录的父目录。

如果想匹配子目录中的文件:

# 子目录有一个 a.txt
# 无效的写法
$ ls *.txt

# 有效的写法
$ ls */*.txt

# 如果孙子目录中有一个 a.txt
$ ls */*/*.txt

即有几层目录,就写几层星号。

方括号扩展

[] 扩展为匹配括号中的任意符,例:[aeiou],即匹配三个字母中的任意一个。

# 存在文件 a.txt、b.txt、ab.txt
$ ls [ab].txt
a.txt b.txt

两种取反变体,表示不匹配括号中的任意字符:

  1. [^...]
  2. [!...]

两种方式的功能是等价的。

# 存在 aaa.txt、bbb.txt、aba.txt 三个文件
$ ls ?[!b]?.txt
aaa.txt

注意:括号中匹配-号时,-号必须写在括号内部的头或尾处,否则无法生效。

# 存在 a-b.txt
$ ls ?[,-]?.txt
a-b.txt
$ ls ?[-,]?.txt
a-b.txt
$ ls ?[,-,]?.txt
ls: cannot access ?[c-d]?.txt: No such file or directory

范围匹配变体:[start-end]

  1. [a-z]:所有小写字母。
  2. [a-zA-Z]:所有小写 & 大写字母。
  3. [a-zA-Z0-9]:所有小写 & 大写字母 & 数字。
  4. [abc]*:所有以a、b、c字符之一开头的文件名。
  5. BACKUP.[0-9][0-9][0-9]:所有以BACKUP.开头,后面是三个数字的文件名。
  6. file[!1-5].txt:匹配文件名最后一个字符非1-5的文件。

大括号扩展

{} 意为集合,表示为分别扩展大括号中的所有项(某个项也可以为一个{}),项之间逗号分隔,并且前后逗号前后不能有空格,否则会被 Bash 理解为这是多个参数。

  1. echo {A,B,C} 输出为:A、B、C。
  2. echo {j{p,pe}g,png},支持嵌套(先扩展嵌套)。输出为:jpg、jpeg、png。
  3. echo A{,B}C,支持空值项。输出为:AC ABC。

与其他模式联用:

$ echo /bin/{cat,b*}
/bin/cat /bin/b2sum /bin/base32 /bin/base64 ... ...

# 等同于
$ echo /bin/cat;echo /bin/b*

上面例子,会先进行大括号扩展,然后进行*扩展。所以在联用时,大括号扩展的优先级是较高的。

由于大括号扩展不是文件名扩展,所以它总是会扩展的。这与方括号扩展完全不同,如果匹配的文件不存在,方括号就不会扩展。但大括号扩展不会考虑文件是否存在,这一点要注意区分。

# 不存在 a.txt 和 b.txt
$ echo [ab].txt
[ab].txt

$ echo {a,b}.txt
a.txt b.txt

上面例子中,如果不存在a.txt和b.txt,那么[ab].txt就会变成一个普通的文件名,而{a,b}.txt可以照样扩展。

范围匹配变体:{start..end}

  1. {a..e}:a、b、c、d、e
  2. {0..3}:0、1、2、3(正序)
  3. {3..0}:3、2、1、0(逆序)
  4. {3..3}:3(单值)
  5. {a..3}{a..3}(无法识别)
  6. {08..11}:08 09 10 11(补0)、{01..110}:001、002、003 … 010 … 020 … 100、101、102 … 110(根据最大值的位数自动补0)。

范围匹配变体:{start..end..step}

  1. {0..10..2}:0 2 4 6 8 10

大括号连用:

# 循环的效果
$ echo {a..b},{1..2}
a,1 a,2 b,1 b,2

# 变更连接符
$ {a..b}-{1..2}
a-1 a-2 b-1 b-2

# 嵌套中的连用不会产生上面的循环效果
$ echo {{a..b},{1..2}}
a b 1 2

连用例子:根据年、月份创建目录

$ mkdir {2020..2022}-{01..12}
$ ls
2020-01  2020-03  2020-05  2020-07 ... 2022-04  2022-06  2022-08  2022-10  2022-12

大括号可以用于多字符的模式,方括号只能匹配单字符。
大括号扩展的常见用途为新建一系列目录。
$ mkdir {2007…2009}-{01…12}

变量扩展

Bash 将美元符号$开头的词元视为变量,将其扩展成变量值,后面的 变量 一节中会详细说明。

$ echo $SHELL
/bin/bash

# 可以放在括号中
$ echo ${SHELL}
/bin/bash

# 变量若不存在,什么都不输出
$ echo ${SHELL233}

子命令扩展

$(...) 可以扩展成另一个命令的运行结果,将括号内的命令的所有输出都会作为返回值。

$ date
Tue Oct 11 13:19:58 CST 2022

$ echo $(date)
Tue Oct 11 13:19:58 CST 2022

还有另一种较老的语法,子命令放在反引号之中,也可以扩展成命令的运行结果。

$ echo `date`
Tue Oct 11 13:19:58 CST 2022

$(...) 可以嵌套,比如:ls $(pwd),将 pwd 命令返回值作为 ls 命令的参数。

# 查看当前目录下的内容
$ ls $(pwd)

# 输出当前目录下的内容
$ echo $(ls $(pwd))

算术扩展

详细见 Blog:Shell 学习(6)Bash 的算术扩展

字符类

  1. [[:class:]] 表示一个字符类,扩展成某一类特定字符之中的一个。常用的字符类如下。
  2. [[:alnum:]]:匹配任意英文字母与数字
  3. [[:alpha:]]:匹配任意英文字母
  4. [[:blank:]]:空格和 Tab 键。
  5. [[:cntrl:]]:ASCII 码 0-31 的不可打印字符。
  6. [[:digit:]]:匹配任意数字 0-9。
  7. [[:graph:]]:A-Z、a-z、0-9 和标点符号。
  8. [[:lower:]]:匹配任意小写字母 a-z。
  9. [[:print:]]:ASCII 码 32-127 的可打印字符。
  10. [[:punct:]]:标点符号(除了 A-Z、a-z、0-9 的可打印字符)。
  11. [[:space:]]:空格、Tab、LF(10)、VT(11)、FF(12)、CR(13)。
  12. [[:upper:]]:匹配任意大写字母 A-Z。
  13. [[:xdigit:]]:16进制字符(A-F、a-f、0-9)。
$ echo [[:upper:]]*

上面命令输出所有大写字母开头的文件名。

字符类的第一个方括号后面,可以加上感叹号!,表示否定。比如,[![:digit:]]匹配所有非数字。

$ echo [![:digit:]]*

上面命令输出所有不以数字开头的文件名。

shopt 命令

Shell option,这个命令可调整 Bash 的行为,开启或关闭一些 Bash 的功能项。

# 查看所有选项的状态
shopt

# 查询某个参数关闭还是打开
$ shopt [option]

# 查询某个参数关闭还是打开
$ shopt -q [option]
$ echo $?

不会直接输出查询结果,而是通过命令的执行退出码($?)表示查询结果。
如果状态为0,表示该参数打开;如果为1,表示该参数关闭。
$? 这个功能符号会在:Shell 学习(5)Bash 变量 中的特殊变量中详细说到。

# 打开某个参数
$ shopt -s [option]

# 关闭某个参数
$ shopt -u [option]
常用参数

1、dotglob:扩展结果包含隐藏的文件(前面提到的.开头的文件),默认情况下,扩展结果中是无法查询到隐藏文件的。

# 直接通过 * 扩展符查询
$ ls *
a.txt

# 开启 dotglob 项
$ shopt -s dotglob

# 在通过扩展符查看
$ ls *
a.txt .hidden.txt

不通过扩展符查看:

$ ls ./
a.txt

# 需要通过 -a 参数查看隐藏文件
$ ls -a
a.txt .hidden.txt

2、nullglob:让通配符不匹配任何文件名时,返回空字符

默认情况下,通配符不匹配任何文件名时,会保持不变。

$ rm b*
rm: 无法删除'b*': 没有那个文件或目录

上面例子中,由于当前目录不包括b开头的文件名,导致b*不会发生文件名扩展。也就是说 rm 命令接收到的参数就为b*

$ shopt -s nullglob
$ rm b*
rm: 缺少操作数

上面例子中,由于没有b*匹配的文件名,所以rm b*扩展成了 rm,导致报错变成了"缺少操作数"。

3、failglob: 通配符不匹配任何文件名时,Bash 会直接报错,而不是让各个命令去处理

$ shopt -s failglob
$ rm b*
bash: 无匹配: b*

打开failglob以后,由于b*不匹配任何文件名,Bash 直接报错了,不会在执行 rm 命令(注意错误信息的提示者)。

4、extglob:使得 Bash 支持 ksh 的一些扩展语法。默认是打开的。

$ shopt extglob
extglob        	on

主要应用是支持量词语法,量词语法会在下面中详细说明。

5、nocaseglob:让通配符扩展不区分大小写。

$ shopt -s nocaseglob
$ ls /windows/program*
/windows/ProgramData
/windows/Program Files
/windows/Program Files (x86)

6、globstar:可以使得**匹配零个或多个子目录。该参数默认是关闭的。

在上面的 * 字符扩展 小节中,如果想扩展多级目录,需要:

$ ls *.txt */*.txt */*/*.txt
a.txt  sub1/b.txt  sub1/sub2/c.txt

这是因为*只匹配当前目录,如果要匹配子目录,只能一层层写出来。

打开globstar参数以后,**匹配零个或多个子目录:

$ shopt -s globstar
$ ls **/*.txt
a.txt  sub1/b.txt  sub1/sub2/c.txt

量词语法

需要先开启extglob功能项:

$ shopt -s extglob

语法:

  1. ?(pattern-list):模式匹配零次或一次。
  2. *(pattern-list):模式匹配零次或多次。
  3. +(pattern-list):模式匹配一次或多次。
  4. @(pattern-list):只匹配一次模式。
  5. !(pattern-list):匹配给定模式以外的任何内容,即取非。
# 匹配零个或一个点
$ ls abc?(.)txt
abctxt abc.txt
# 匹配零个或一个def
$ ls abc?(def)
abc abcdef
# 匹配文件有且只有一个.txt或.php后缀名
$ ls abc@(.txt|.php)
abc.php abc.txt
# 匹配文件有一个或多个.txt后缀名
$ ls abc+(.txt)
abc.txt abc.txt.txt
# 匹配文件的名称最后一位不为b
$ ls a!(b).txt
a.txt ac.txt
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值