【问题标题】:perl open files with variable in filenameperl 打开文件名中带有变量的文件
【发布时间】:2018-05-30 03:13:45
【问题描述】:

我尝试打开文件夹中的文件列表。最终我想在其中插入一个 HTML 片段。到目前为止,我可以打开文件夹并读取数组中的列表。没关系。但是当我尝试使用变量作为文件名打开文件时,每次都会收到一条错误消息(“权限被拒绝”)。无论我是否使用 $_ 或其他将列表项的值放入变量的变体。我在这里找到了类似的问题,但到目前为止还没有找到解决方案。这是我的代码:

use strict;
use warnings;

my ($line);

opendir (FOLDER,"path/to/folder/./") or die "$!";
my @folderlist = readdir(FOLDER);
my @sorted_folderlist = sort @folderlist;
close(FOLDER);

foreach (@sorted_folderlist) {
  my $filename = $_;
  open (READ, ">", "path/to/folder/$filename") or die "$!";
  # do something
  close (READ);
}

这里有什么错误?以及如何使用变量作为文件名打开文件?

普约恩

这是我为了回答 1 而更改的代码:

my $dh; 
opendir $dh, "dir/./" or die ... 
my @folderlist = grep { -f "dir/$_" } readdir $dh; 
close $dh; 
my @sorted_folderlist = sort @folderlist; 

foreach my $filename (@sorted_folderlist) { 
  open my $fh, "<", "dir/$filename" or die ... 
  open my $writeto, ">", "new/$filename" or die ... 
  print $writeto "$fh"; 
  close $fh; 
  close $writeto; 
}

【问题讨论】:

  • 我已经编辑了您的两个代码示例以添加缩进。不客气,但请以后自己做。适当的缩进是帮助人们理解你的代码的重要工具。如果您要求一大群陌生人阅读和理解您的代码,那么让他们尽可能简单肯定是礼貌的做法。
  • 嗯。在您应用@tinita 的提议后,您编辑了您的问题并显示了您的工作代码,这非常好。我明白你想分享它并告诉我们“看看我是如何让它工作的”。但现在这不再是一个问题。它已经包含一个答案。人们会试图找出你的第二个代码 sn-p 出了什么问题,然后发现……什么也没有。 /// 我不确定如何处理。有人有想法吗? @戴夫?
  • 嗯,我编辑了我的问题,因为我实际上认为表现出我对答案表示赞赏是有礼貌的。如果合理的话,我可以删除第二个代码示例。 @DaveCross:当然,我会看看的。
  • @PerlDuck:我认为这个问题仍然很清楚。并且第二个代码示例中仍然存在明显的问题-例如print $writeto "$fh"很奇怪:-)
  • @Pjoern 你添加了print $writeto "$fh";。打印到$writeto 好吧,但它打印的是文件句柄$fh 本身(不需要在"" 下评估它,也不会改变任何东西。);所以它会打印出类似GLOB(0x1820a68) 的东西。相反,从该输入中读取,处理该行,然后将其打印到输出 while(my $line = &lt;$fh&gt;) { process-$line; print $writeto $line; }

标签: arrays perl file


【解决方案1】:

您的代码中有几个问题。

第一个导致错误的原因可能是readdir() 也会返回...,但这些是目录。所以你试着写信给directory/.

其次,您的错误消息包含$!,这很好,但您不输出文件名。那么你就会看到你的错误了。

第三,你调用一个你打开的文件句柄来写READ

另外,现在你应该使用词法文件句柄。

use strict;
use warnings;

opendir my $dh, "dir" or die $!;
# read all files, not directories
my @folderlist = grep { -f "dir/$_" } readdir $dh;
close $dh;
my @sorted_folderlist = sort @folderlist;

foreach my $filename (@sorted_folderlist) {
    open my $fh, ">", "dir/$filename" or die "Could not write to dir/$filename: $!";
    print $fh "foo...\n";
    close $fh;
}

【讨论】:

  • @tinita:感谢您的回答,通过您的示例,我想我可以让我的代码正常工作 - 尽管我并不了解每个细节。我对 readdir() 的使用是我认为的关键错误。也感谢您对现代代码的提示:)。到目前为止,我已经在我的问题(@Dave Cross)中发布了我的代码。我正在努力。
【解决方案2】:

您已在 tinita's answer 中清除了错误和良好做法。

我想评论一个可能的改进,即使用完整路径构建文件列表。

  • readdir 的输出上使用map 将目录添加到每个文件,然后对其进行过滤

    my @files = grep { -f } map { "$dir/$_" } readdir $dh;
    

    或者,在map 块中实现grep 的(过滤)功能

    my @files = map { my $fp = "$dir/$_"; -f $fp ? $fp : () } readdir $dh;
    

    如果$fp 不是-f,我们将返回一个空列表,该列表在输出中被展平,从而消失。

  • 使用glob,直接给你完整路径

    use File::Glob ':bsd_glob';
    my @files = grep { -f } glob "$dir/*";
    

    File::Globbsd_glob() 覆盖 glob 并很好地处理名称中的空格。

请注意区别:glob 中的 * 不会选择以点 (.) 开头的条目。如果您也想要这些,请将其更改为 glob "$dir/{*,.*}"。请参阅链接的 File::Glob 文档。


更新 更正问题的编辑(添加),也在 cmets 中讨论

一旦完整路径文件名被读入@files

foreach my $file (@files) {
    my $out_file = 'new_' . $file;
    open my $writeto '>', $outfile or die "Can't open $outfile: $!";
    open my $fh,     '<', $file    or die "Can't open $file: $!";
    while (my $line = <$fn>) {
        # process $line and build what need be written to the new file
        my $new_line = ...   
        print $writeto $new_line;
    }
    close $writeto;
    close $fh;
}

(或者,如果文件名不是完整路径,则在 open 调用中构建完整路径。)

文件句柄在再次打开时会关闭,因此您只需要为最后一个文件提供明确的close。然后你可以在循环之前声明my ($fh, $writeto);变量,使用open $fh ...(没有my),然后关闭文件句柄。但是你有这些变量在foreach 范围之外。

文档:


glob "$dir/*" 带有注入错误的可能性,对于非常特殊的目录名称。 虽然这只能通过相当不寻常的名称来实现,但使用 escape sequence 会更安全

my @files = grep { -f } glob "\Q$dir\E/*"

由于这也会转义空格,因此不需要带有 bsd_glob()File::Glob

【讨论】:

  • glob 是扫描目录的首选方法; File::Find 也是扫描子目录的首选。
猜你喜欢
  • 2014-10-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多