通配模式
Perl中使用glob
函数来通配文件,支持的通配语法如下:
\ 转义下一个通配元字符
[] 字符类,可通配中括号中的单个字符,可使用范围模式
{} 多模式,例如`a{a,b}c`可通配aac和abc
* 匹配任意长度的字符(无法通配文件名开头的点)
? 匹配任意单个字符(无法通配文件名开头的点)
~ 家目录
例如,glob("*.sh")
将匹配当前目录下的所有sh脚本文件。
glob
函数在列表上下文中返回所有通配成功的文件列表,在标量上下文则迭代每个被通配成功的文件名,迭代完成后返回undef值。
# 当前目录下所有文件(不会递归子目录)
while(glob("*")){
say $_;
}
注意,glob
内部使用的是Csh通配规则,因此会识别通配表达式中的空格,并根据空格划分token。例如,glob("*.c .*.c")
是两个通配表达式,表示通配c文件和以点开头的c文件,它们是逻辑或的关系。glob("* .*")
表示通配当前目录所有文件(包括.
和..
)。
如果想明确表示通配表达式中的空格是通配表达式中的一部分,而不是将其作为分隔符,需要将通配表达式整体进行引用包围。例如glob('"*e f*"')
或者glob(q("*e f*"))
。
因此,当通配符中使用了变量时,最好将它使用引号包围起来,避免变量值中包含空格。
glob通配时不会递归到子目录中去通配,如果也想要搜索子目录中的文件,可考虑使用find而不是通配。
通配操作也并非总是用来通配,它的{}
通配语法也可以用来生成字符串:
$ perl -E '@arr = glob("a{b,c}d{e,f}h");say "@arr"'
abdeh abdfh acdeh acdfh
<>中的通配语法
<>
除了可以用来读取文件句柄,还可以用来进行通配操作。
当perl发现<>
中的不是一个标量变量(如$fh
)或不是一个裸句柄变量(如STDIN/ARGV),就会将<>
中的东西当作通配符进行文件通配。
while(<"*">){ # 通配当前目录的所有非隐藏文件
say "$_";
}
注意,<>
中的通配符不像glob()
一样会因为空格而划分通配表达式,<>
中的通配表达式在转换为内部的glob通配表达式时,默认会在外层加上一层引号。因此,<>
中的通配表达式总是单个表达式整体。
# 错误的写法
# 含义是匹配文件名中带有空格和点的文件名
while(<"* .*">){}
File::Glob
默认情况下,glob函数使用的是Csh的通配规则,<>
方式的通配内部使用的也是glob,因此也是csh通配。
但有时候,使用csh通配功能会比较受限,如果想要使用功能更丰富的通配,可以使用File::Glob
模块。File::Glob
模块提供了csh_glob
和bsd_glob
,csh_glob即核心模块中原始的glob函数,bsd_glob功能则更丰富,它支持为通配规则指定修饰符,比如不区分大小写、家目录扩展等修饰符。
需注意的是,<>
方式的通配是根据当前的通配规则来生成文件路径的,因此导入File::Glob
的功能时,可能会对<>
产生影响,例如导入:nocase
属性,将使得<>
中的通配不区分大小写,导入:glob
将覆盖原始的glob函数,使得<>
中的通配采用File::Glob
中:glob
规则。
使用File::Glob
方式很简单:
#!/usr/bin/env perl
use 5.010;
use File::Glob qw(:bsd_glob :csh_glob :globally :nocase);
# 导入:nocase后,通配将不区分大小写
# `<>`方式的通配将不区分大小写
while(<*.LOG>){ say $_; }
# 明确表示使用globally通配,它等价于原生的glob函数
for(globally("*.LOG")){ say $_; }
# 明确表示使用bsd_glob通配
for(bsd_glob("*.LOG")){ say $_; }
# 明确表示使用csh_glob通配
for(csh_glob("*.LOG")){ say $_; }
csh_glob
和原始的glob一样,会将通配表达式中的空格识别为分隔符。bsd_glob
则不会分隔为多个通配表达式,而是将通配表达式看作是整体。因此,如果想要在bsd_glob
中指定多个通配模式,只能使用{}
语法,例如bsd_glob("{a*,b*}")
等价于csh_glob("a* b*")
。
bsd_glob
允许指定两个参数,一个是必选的通配表达式,第二个参数是可选的通配修饰符:
my $homedir = bsd_glob('~jrhacker', GLOB_TILDE | GLOB_ERR);
if (GLOB_ERROR) {
# An error occurred expanding the home directory.
}