使用 FIND & EXEC 在多个文件上执行需要文件名的 Perl 脚本

Using FIND & EXEC to execute a filename-required Perl script over multiple files

我在 unix / Linux 目录中存储了数百个 CSV 文件。他们的名字遵循以下格式:MMYYY_foo.csv。例如,

072019_foo.csv
122018_foo.csv

我正在尝试使用 Perl 脚本将它们单独编译并转换为 XML。该命令采用 ./script.pl MMMMYY_foo 形式,因此在上面的示例中需要执行以下命令:

./script.pl 072019_foo
./script.pl 122018_foo

我不是在 UNIX / LINUX 中为每个文件单独执行 perl 脚本,而是尝试遍历文件,将它们传递给 perl 脚本进行编译。在其他来源中对 SO 进行了繁琐的研究,我得出以下结论......

find . -type -f -name '*.csv' -exec perl script.pl $('-printf "%f\n"') {} \;

但是这不起作用。而是输出多个“.xml”。毫无疑问,文件名(减去路径和扩展名)没有像上面的代码示例那样正确地传递给脚本。我尝试了多种变体 ...

$(-printf "%f\n"')

我知道我的问题就在这里。在许多情况下,我只是得到多个“.xml”。我觉得我正处于找到解决方案的风口浪尖。只是我不了解 -exec 之外的命令行功能的适当性。所以我想寻求任何帮助,看看是否有人知道解决方案。

该命令在执行任何其他操作之前先执行一个名为 -printf "%f\n" 的文件,这显然失败了。

我想你想要的是

find . -type -f -name '*.csv' -printf '%f[=10=]' | xargs -r0 ./script.pl

但这有两个问题:

  • 你删除了路径,所以进行递归搜索没有任何意义(就像 find 默认情况下那样)。您已在评论中确认不需要进行递归搜索。
  • 这仍然通过了您要删除的扩展程序。

因此,以下是您寻求的解决方案:

find . -maxdepth 1 -name '*.csv' -printf '%f[=11=]' |
   perl -0lpe's/\.[^.]*\z//' |
   xargs -r0 ./script.pl

或者只是

perl -0le'print s/\.[^.]*\z//r for @ARGV' -- *.csv |
   xargs -r0 ./script.pl

或者只是

perl -e'system("./script.pl", s/\.[^.]*\z//r) for @ARGV' -- *.csv

或者只是

perl -e'system("./script.pl", s/\.[^.]*\z//r) for glob("*.csv")'

第一个和最后一个将比其他两个更好地处理非常长的文件列表。

您可以像这样使用 GNU Parallel 非常简单地并行完成它们:

parallel --dry-run perl script.pl {.} ::: *csv

示例输出

perl script.pl 072019_foo
perl script.pl 122018_foo

如果看起来正确,请备份您的文件,然后 运行 再次备份,而无需 --dry-run 真正做到这一点。

您可以使用 parallel --bar ...

添加进度条

OP 的 find 示例表明目录中的所有 cvs 文件都需要处理。

假设不需要递归到目录结构。

bash shell 的强大功能可用于此目的,在传递给脚本之前要删除文件扩展名

for f in *.cvs
do
   ./script.pl ${f%.*}
done

如果此任务将定期重复,上面的脚本可以存储为 shell 脚本或创建的其他 perl 包装脚本

#!/usr/bin/env perl

use strict;
use warnings;

my $re = qr/(\d{6}_foo).cvs/;

for ( glob('./*.cvs') ) {
        system('./script.pl', ) if /$re/;
}

find 命令的自然行为是递归到目录结构中。 OP 应在 post.

中指明是否需要递归

建议:熟悉3.5.3 Shell Parameter Expansion, How To Use Bash Parameter Substitution Like A Pro