macOS下Perl使用File::Find时abs_path遇相对路径返回undef问题
在macOS上运行的Perl脚本接收一个目录参数,随后调用File::Find的&wanted()函数处理目录内容。
若传入绝对路径(例如"/Users/sw/Downloads/FGT")则运行正常,但传入相对路径(比如从上述目录运行时传入".")会出现错误。
错误发生在处理子目录中的文件时:总是特定子目录(第一个二级子目录)中的第一个文件触发错误(若在该目录中创建或touch一个字母顺序更早的新文件,新文件会触发错误),此前的许多文件都能正常处理。
为何在处理该特定文件(第一个二级子目录的首个文件)时,abs_path调用会返回undef?
完整代码
#!/usr/local/bin/perl use warnings; use English; use vars qw ( $VERSION ); use Cwd 'abs_path'; use File::Find; find({ wanted => \&processFile, follow => 0 }, $ARGV[0]); #we do NOT follow symbolic links exit(); sub processFile { if (substr($_,0,1) eq ".") { return; } #ignore entry starting with dot print "\nPROCESSING WITH:\n"; print ">> \$File::Find::dir = <<$File::Find::dir>>\n"; print ">> \$_ = <<$_>>\n"; print ">> \$File::Find::name=<<$File::Find::name>>\n"; my $dirAbsPath=abs_path($File::Find::dir); die "ERROR: undef abs_path() using Find::File::dir value!\n" if ! defined $dirAbsPath; print "Dir Abs Path is <<$dirAbsPath>>\n"; }
输出
PROCESSING WITH: >> $File::Find::dir = <<.>> >> $_ = <<L1DIR>> >> $File::Find::name=<<./L1DIR>> Dir Abs Path is <</Users/sw/Downloads/FGT>> PROCESSING WITH: >> $File::Find::dir = <<./L1DIR>> >> $_ = <<LPT1.csv>> >> $File::Find::name=<<./L1DIR/LPT1.csv>> Dir Abs Path is <</Users/sw/Downloads/FGT/L1DIR/L1DIR>> PROCESSING WITH: >> $File::Find::dir = <<./L1DIR>> >> $_ = <<L2DIR2>> >> $File::Find::name=<<./L1DIR/L2DIR2>> Dir Abs Path is <</Users/sw/Downloads/FGT/L1DIR/L1DIR>> PROCESSING WITH: >> $File::Find::dir = <<./L1DIR/L2DIR2>> >> $_ = <<abc>> >> $File::Find::name=<<./L1DIR/L2DIR2/abc>> ERROR: undef abs_path() using Find::File::dir value!
当前目录树
sw@Max FGT % tree . . └── L1DIR ├── L2DIR1 │ └── LD.csv ├── L2DIR2 │ ├── abc │ └── x.csv ├── L2DIR3 │ └── xsym2 -> T2/x.csv └── LPT1.csv
核心问题
从输出能看到,处理./L1DIR下的文件时,abs_path返回了错误路径/Users/sw/Downloads/FGT/L1DIR/L1DIR——这是因为File::Find的默认行为是进入遍历的目录(修改当前工作目录),但$File::Find::dir存储的是相对于脚本初始工作目录的路径。
当脚本进入L1DIR后,当前工作目录变成了/Users/sw/Downloads/FGT/L1DIR,此时调用abs_path($File::Find::dir)解析./L1DIR,就会得到/Users/sw/Downloads/FGT/L1DIR/L1DIR——这个路径根本不存在,后续解析更深的./L1DIR/L2DIR2自然会返回undef。
而传入绝对路径时,$File::Find::dir本身就是绝对路径,不受当前工作目录变化影响,所以不会出错。
解决方法
有两种简单的修复方式:
- 禁止
File::Find切换工作目录
在find函数的参数中添加no_chdir => 1,这样File::Find不会修改当前工作目录,$File::Find::dir的相对路径解析始终基于初始工作目录:find({ wanted => \&processFile, follow => 0, no_chdir => 1 }, $ARGV[0]); - 基于完整路径解析绝对目录
直接对$File::Find::name(包含完整相对路径的文件名)调用abs_path,再提取目录部分:my $fullAbsPath = abs_path($File::Find::name); die "ERROR: undef abs_path() for $File::Find::name!\n" if !defined $fullAbsPath; my $dirAbsPath = $fullAbsPath; $dirAbsPath =~ s/\/[^\/]+$//; # 移除文件名,保留目录路径
内容的提问来源于stack exchange,提问作者strapworn

