在Unix下进行文件夹的内容展示或者文件查找的时候,会大量的使用*
来对文件名进行通配扩展,那么在使用Perl进行搭建流程的时候同样支持文件名通配。在Shell下,通配使用的是glob,一种特殊的模式匹配,也是最常见的通配符扩展,可以理解成精简版的正则表达式;Perl中的glob通配方式和Shell的通配方式是完全一致,因为Perl的glob函数直接调用系统的$SHELL来进行通配的。
glob通配函数
此处要注明,通配符 / glob 模式通常用来匹配目录以及文件,而不是文本,其语法:
元字符 | 解释 |
---|---|
[] | 字符类,匹配中括号中的任一字符 |
* | 匹配任意个字符 |
? | 匹配任意单个字符,不包含0个 |
[list] | 匹配指定范围(list)内任意单个字符,可以是单个字符组成的集合 |
[^list] | 匹配指定范围(list)外的任意单个字符或字符集合 |
{!list} | [^list] 一致 |
[str1,str2...] | 匹配 srt1 或者 srt2 或者更多字符串,也可以是集合 |
~ | 匹配家目录(/home/name) |
注意:
- []]支持[0-9] [a-z] [A-Z] [A-z]类似的范围通配,其中[A-z]等价于[A-Za-z]
- 不支持[^]取反
- 特别需要注意的是[1-34]通配的是[1234],而不是1到34,因为中括号只能匹配单个字符
例子:
my @F = glob '*'; print join ("\n",@F); # 匹配当前目录下所有非"."开头的隐藏文件
my @F = glob '.* *'; print join ("\n",@F); # 匹配当前目录下所有文件,包括"."开头的隐藏文件
my @F = glob 'test.p[ly]'; print join ("\n",@F); # 匹配test.pl或test.py文件
my @F = glob '[0-9][0-9]*.pl'; print join ("\n",@F); # 匹配两个数值开头的pl文件
my @F = glob '~/test.p?'; print join ("\n",@F); # 匹配家目录下后缀以p开头,后面还有一个字符的文件
my @F = glob '~/*.sh'; print join ("\n",@F); # 匹配家目录下的所有sh文件
在glob函数中,如果想要匹配包含空格的文件名,必须将其使用引号(单/双引号皆可)包围,如:匹配"123 test.txt"文件:
glob '"123 t*.txt"';
glob "'123 t*.txt'";
尖括号<>通配写法
尖括号表达式用于通配和glob的实现是一致的,仅仅尖括号改成了glob函数。例如匹配根目录下的".sh"文件:
my @F = glob '~/*.sh';
my @F = <~/*.sh>;
my $dir = "/home/luna";
my @F = glob '$dir/*.sh';
my @F = <$dir/*.sh>;
在这里一定要写正确通配符的格式,因为在perl中尖括号有时候是文件句柄,此处的解析规则是:假如尖括号内的内容满足标识符规则(文件句柄的名称要满足此规则),则会解析为文件句柄,否则解析成通配符。
# 文件名通配符
my @F = <path/*>;
my @F = <$dir/*>;
# 读取文件句柄
my @li = <path>;
my @li = <$line>;
文件查找:find2perl脚本
在Unix下有一个方便查找文件的工具——find。Perl提供了一个find2perl的工具(安装perl时会自带安装),它可以将find查找文件时的表达式转换成Perl对应的查找语句。
find2perl的选项和用法和find的用法99%都一样,只有几项额外的是find2perl自身提供的,但这样的选项非常少。
但是请注意,find2perl不是文件查找工具,只是将我们写的find命令表达式转换为等价的Perl文件查找语句。
例如,搜索/etc目录下所有".conf"结尾的文件,find命令的表达式如下:
find /etc -type f -name "*.conf"
执行find2perl:
find2perl /etc -type f -name "*.conf"
屏幕会出现一堆代码:
#! /usr/bin/perl -w
eval 'exec /usr/bin/perl -S $0 ${1+"$@"}'
if 0; #$running_under_some_shell
use strict;
use File::Find ();
# Set the variable $File::Find::dont_use_nlink if you're using AFS,
# since AFS cheats.
# for the convenience of &wanted calls, including -eval statements:
use vars qw/*name *dir *prune/;
*name = *File::Find::name;
*dir = *File::Find::dir;
*prune = *File::Find::prune;
sub wanted;
# Traverse desired filesystems
File::Find::find({wanted => \&wanted}, '/etc');
exit;
sub wanted {
my ($dev,$ino,$mode,$nlink,$uid,$gid);
(($dev,$ino,$mode,$nlink,$uid,$gid) = lstat($_)) &&
-f _ &&
/^.*\.conf\z/s
&& print("$name\n");
}
可以看出,上面生成了一个wanted子程序,可以保存起来,以后需要在/etc下查找"*.conf"文件时,只需调用wanted子程序即可:
find2perl /etc -type f -name "*.conf" > FindConfInEtc.pl
chmod +x FindConfInEtc.pl
./FindConfInEtc.pl
/etc/ltrace.conf
/etc/pnm2ppa.conf
/etc/brltty.conf
/etc/signond.conf
/etc/sysctl.conf
/etc/gai.conf
/etc/apg.conf
......
References
—— dulunar 后记于 2020.12