使用 FIND 和 EXEC 对多个文件执行文件名要求的 Perl 脚本

Posted

技术标签:

【中文标题】使用 FIND 和 EXEC 对多个文件执行文件名要求的 Perl 脚本【英文标题】:Using FIND & EXEC to execute a filename-required Perl script over multiple files 【发布时间】:2021-02-28 23:45:01 【问题描述】:

我有数百个 CSV 文件存储在 unix / Linux 目录中。他们的名字遵循以下格式: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 脚本进行编译。在其他来源中进行繁琐的研究后,我得出以下结论......

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

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

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

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

【问题讨论】:

【参考方案1】:

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

我认为你想要类似的东西

find . -type -f -name '*.csv' -printf '%f\0' | xargs -r0 ./script.pl

但这有两个问题:

你去掉了路径,所以递归搜索没有任何意义(就像find默认的那样)。您已在 cmets 中确认不需要进行递归搜索。 这仍然会传递您要删除的扩展名。

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

find . -maxdepth 1 -name '*.csv' -printf '%f\0' |
   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")'

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

【讨论】:

【参考方案2】:

您可以使用 GNU Parallel 以非常简单的方式完成所有操作,如下所示:

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

样本输出

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

如果看起来正确,请备份您的文件并在不使用 --dry-run 的情况下再次运行它以真正做到这一点。

您可以使用parallel --bar ...添加进度条

【讨论】:

马克,感谢您的回答。遗憾的是,我没有 GNU Parallel,也没有能力将它添加到我公司的 IT 基础设施中。 :-/ 耻辱 - 在 CPU 越来越胖(更多内核)而不是更高(更多 GHz)的时代,它非常适用。顺便说一句,它实际上只是一个 Perl 脚本,就像其他任何脚本一样。【参考方案3】:

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/(\d6_foo).cvs/;

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

find 命令的自然行为是递归到目录结构。 OP 应该在帖子中说明递归是否可取。

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

【讨论】:

以上是关于使用 FIND 和 EXEC 对多个文件执行文件名要求的 Perl 脚本的主要内容,如果未能解决你的问题,请参考以下文章

Linux系列find命令使用

find与xargs配合使用

find命令处理之exec与xargs区别

find 命令详解

Linux中find常见用法示例 ·find path -option [ -print ] [ -exec -ok command ] {} ;

如何将 GNU 与 find -exec 并行使用?