【问题标题】:Perl search for files and rename them while copying to different directory [duplicate]Perl搜索文件并在复制到不同目录时重命名它们[重复]
【发布时间】:2015-04-14 11:17:50
【问题描述】:

我正在尝试使用以下代码搜索 .txt 文件并在将其复制到不同目录时重命名它们。

#!/bin/perl
use File::Basename;
@txtfiles = <*/*.txt>;
foreach my $file(@textfiles){
    $dir = dirname($file);
    $file = basename($file);
    $file =~ s/(\d+)/$dir/; //renaming number with $dir
`mkdir -p summary` unless -d summary;
`cp $file summary`;
}

上面的代码给出了错误,说没有要复制的文件,但是每行的打印语句正确显示(重命名的文件名)

【问题讨论】:

  • 总是 use strict; use warnings;!
  • 在 Perl 中评论任何行使用# 而不是//
  • 感谢您的建议,我会改正的。你能帮我看看代码哪里出错了。

标签: perl


【解决方案1】:

不!

不要使用系统命令——尤其是你可以使用 Perl 命令。

`mkdir -p summary` unless -d summary;
`cp $file summary`;

使用 Perl 命令!

use File::Copy;       # Standard Perl Module. USE IT!
...
mkdir 'summary' unless -d 'summary';   # No need for `-p`
copy $file, 'summary';

这是修改后的脚本:

#! /usr/bin/env perl

use strict;
use warnings;
use feature qw(say);

# use File::Basename;  -- No need for this in this version
use File::Copy;

# What files do you want to find?
# Do you want to find the immediate files or the
# files in the subdirectories?

my @text_files = glob('*.txt');  # Immediate files only

mkdir 'summary' if not -d 'summary';   # Make this dir before the loop
for my $file ( @text_files ) {
    next unless -f $file;              # Make sure this is a file you want to copy!
    #
    # No need for 'dirname' and 'basename' with '*.txt' glob
    #
    if ( not copy $file, 'summary' ) { # Check the outcome of this command.
        warn qq(Could not copy file "$file" to "summary".);
    }
}

如果您需要将文件复制到子目录而不是直接目录中,请告诉我们。也许您可以使用File::Path 中的make_tree,或者File::Find 中的find 的Perl 版本。这些是所有 Perl 安装都有的标准 Perl 模块。


附录

我不想要当前目录。这些文件位于一个目录中,即foo/text_v0001.txtfoo/text_v0002.txtfoo_au/text_v0003.txtfoo_au/text_v0004.txt 继续......我想用目录名称替换数字,例如foo/text_v0001.txt 应该重命名为 text_foo.txtfoo/text_v0002.txt 应该重命名为 text_foo.txt (因为在同一个文件夹中我们不能有同名文件,我们可以添加 part2 和第二个文件的结尾,即 text_fooPart2.txt)。

最后一部分是一个笨蛋,它也是一个新的要求。我需要验证不存在同名的文件,如果存在,我需要确保找到下一个可用名称。

如果我发现一个文件已经存在,我会循环递增一个重复文件计数器,直到找到一个不存在的文件名。

#! /usr/bin/env perl

use strict;
use warnings;
use feature qw(say);

use File::Basename;
use File::Copy;
use File::Glob;                        # Improved Glob matching.

use constant {
    DIRECTORY           => 'summary',
};

# What files do you want to find?
# Do you want to find the immediate files or the
# files in the subdirectories?

#
# Let's do a more sophisticated pattern making sure we're catching
# the files we want.
#
my @text_files = glob('*/*.txt');      # Subdirectories only
mkdir DIRECTORY if not -d DIRECTORY;   # Make this dir before the loop

for my $file ( @text_files ) {
    my $dir_name  = dirname $file;
    my $file_name = basename $file;
    say "DEBUG: On '$file'.";
    #
    # Let's make sure that the file name matches the expected
    # pattern. If the substitution doesn't succeed, we assume
    # this file shouldn't be copied, and skip it.
    #
    # I'm serching for a file that has the suffix '_vxxxx.txt' where
    # 'xxxx' is some number. I remove the number and the letter `v`,
    # and add in the directory name.
    #
    if ( not $file_name =~ s/_v(\d+)\.txt$/_$dir_name.txt/ ) {
        warn qq("$dir_name/$file_name" has not been copied.");
        next;
    }
    #
    # If the name matches, make sure it's a file
    #
    if ( not -f $file ) {
        warn qq("$file" is not a file and wasn't copied.");
        next
    }
    #
    # Now make sure file name is unique
    #
    if ( -f DIRECTORY . "/$file_name" ) {         # File name already exists
        say qq(DEBUG: Duplicate File '$file_name' detected!);
        my $dup_file_counter = 2;
        ( my $file_no_suffix = $file_name ) =~ s/\.txt$//;
        #
        # Find a non-matching name
        #
        for (;;) {
            my $new_file_name = $file_no_suffix . "_part_$dup_file_counter.txt";
            say "DEBUG: New file name '$new_file_name'";
            say qq(DEBUG: if ( not -e @{[DIRECTORY]} . "/$new_file_name" ) { );
            if ( not -e DIRECTORY . "/$new_file_name" ) {
                $file_name = $new_file_name;
                last;
            }
            else {
                $dup_file_counter += 1;
            }
        }
    }


    if ( not copy $file, DIRECTORY . "/$file_name" ) { # Check the outcome of this command.
        warn qq(Could not copy file "$file" to directory ") . DIRECTORY . qq(".);
    }
}

【讨论】:

  • 感谢大卫的重播和建议,我需要子目录中的文件。我的要求是在子目录中查找文件并使用找到的目录名修改文件名,然后复制到不同的目录,即摘要
  • 你也需要CURRENT目录下的文件吗?使用*/*.txt 作为模式获取子目录下的文件,而不是当前目录?像foo/bar/barfoo.txt 这样的文件呢?你也想要这个复制吗?我会在您回复后立即更新我的答案。
  • 我不想要当前目录。这些文件位于一个目录中i.e. foo/text_v0001.txt foo/text_v0002.txt foo_au/text_v0003.txt foo_au/text_v0004.txt 继续....我想用目录名称替换数字,例如foo/text_v0001.txt 应该重命名为 text_foo.txtfoo/text_v0002.txt 应该重命名为 text_foo.txt (因为在同一个文件夹中我们不能有同名文件,我们可以添加 part2 和第二个文件的末尾,即 text_fooPart2.txt)。如果我不清楚,请告诉我。
  • 非常感谢大卫。你是天才,它运作良好。你能指导我如何学习这样的编码吗?你能指导我一些关于 perl 的文档/教程和技巧吗?
  • 基本文本称为Llama Book。这是一本快速掌握 Perl 语法和理解基础知识的好书,但它不涉及模块(尤其是标准模块,例如 File::Copy)或 OOP Perl。但是,它会让您很快掌握该语言。
【解决方案2】:

在循环中,您使用的是@textfiles 而不是@txtfiles。使用strict

#!/usr/local/bin/perl
use File::Basename;
use strict;
use warnings;


my @txtfiles = glob("*.txt");
foreach my $file(@txtfiles){
     my $dir = dirname($file);
     $file = basename($file);
     $file =~ s/(\d+)/$dir/; # renaming number with $dir
     `mkdir -p summary` unless -d "summary";
     `cp $file summary`;
}

【讨论】:

  • 这仅适用于当前目录。
  • 是的,你是正确的serenesat ...这仅适用于当前目录。 @Morad 在这里打字时打错了... :)
  • @praneeth 您可以使用 find shell 命令在任何需要的地方搜索 txt 文件
  • 我在使用 cp 操作之前注意到这里的一点,$file 状态将被修改(意味着文件名发生了变化,但物理上它没有在系统上改变)并且在 cp 阶段它试图查找不存在的已修改 $file 名称并抛出错误说没有这样的文件或目录。所以现在我想要两个变量,比如cp filename1 (before modification) filename2 (after renaming files)
猜你喜欢
  • 2018-08-04
  • 1970-01-01
  • 2016-06-01
  • 2015-07-09
  • 1970-01-01
  • 2013-08-25
  • 1970-01-01
  • 2018-08-17
  • 2012-09-29
相关资源
最近更新 更多