Perl中的文件名通配符和文件查找

Perl

Posted by dulunar on December 26, 2020 | 访问量:

在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)

注意:

  1. []]支持[0-9] [a-z] [A-Z] [A-z]类似的范围通配,其中[A-z]等价于[A-Za-z]
  2. 不支持[^]取反
  3. 特别需要注意的是[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

Linux shell 通配符 / glob 模式

骏马金龙

						—— dulunar 后记于 2020.12