gpt4 book ai didi

python - 从包含数百万个文件的目录 (bash/python/perl) 中通过精确匹配有效地查找数千个文件

转载 作者:行者123 更新时间:2023-12-03 18:44:03 25 4
gpt4 key购买 nike

我在 Linux 上,我试图从包含数百万个文件的目录 (SOURCE_DIR) 中找到数千个文件。我有一个需要查找的文件名列表,存储在单个文本文件 (FILE_LIST) 中。该文件的每一行都包含一个名称,对应于 SOURCE_DIR 中的一个文件,并且该文件中有数千行。

## FILE_LIST contain single word file names, each per line
#Name0001
#Name0002
#..
#Name9999

我想将文件复制到另一个目录(DESTINATION_DIR)。我写了下面的循环,里面有一个循环来一一查找。
#!/bin/bash
FILE_LIST='file.list'
## FILE_LIST contain single word file names, each per line
#Name0001
#Name0002
#..
#Name9999

SOURCE_DIR='/path/to/source/files' # Contain millions of files in sub-directories
DESTINATION_DIR='/path/to/destination/files' # Files will be copied to here


while read FILE_NAME
do
echo $FILE_NAME
for FILE_NAME_WITH_PATH in `find SOURCE_DIR -maxdepth 3 -name "$FILE_NAME*" -type f -exec readlink -f {} \;`;
do
echo $FILE
cp -pv $FILE_NAME_WITH_PATH $DESTINATION_DIR;
done
done < $FILE_LIST

这个循环需要很多时间,我想知道是否有更好的方法来实现我的目标。我搜索了,但没有找到解决我的问题的方法。如果已经存在,请直接向我提供解决方案,或者建议对上述代码进行任何调整。如果另一种方法甚至是 python/perl 解决方案,我也很好。感谢您的时间和帮助!

最佳答案

备注 下面添加了处理不同目录中相同名称的代码

需要找到要复制的文件,因为它们没有给出路径(不知道它们在哪个目录中),但是重新搜索每个文件非常浪费,大大增加了复杂性。
相反,首先为每个文件名构建一个带有完整路径名的哈希。
一种方式,使用 Perl,利用快速核心模块 File::Find

use warnings;
use strict;
use feature 'say';

use File::Find;
use File::Copy qw(copy);

my $source_dir = shift // '/path/to/source'; # give at invocation or default

my $copy_to_dir = '/path/to/destination';

my $file_list = 'file_list_to_copy.txt';
open my $fh, '<', $file_list or die "Can't open $file_list: $!";
my @files = <$fh>;
chomp @files;


my %fqn;
find( sub { $fqn{$_} = $File::Find::name unless -d }, $source_dir );

# Now copy the ones from the list to the given location
foreach my $fname (@files) {
copy $fqn{$fname}, $copy_to_dir
or do {
warn "Can't copy $fqn{$fname} to $copy_to_dir: $!";
next;
};
}
剩下的问题是关于可能存在于多个目录中的文件名,但我们需要给出一个规则来决定接下来该做什么。†
我忽略了问题中使用了最大深度,因为它无法解释并且在我看来是与极端运行时相关的修复(?)。此外,文件被复制到“平面”结构中(不恢复其原始层次结构),从问题中获取线索。
最后,我只跳过目录,而其他各种文件类型都有自己的问题(复制链接需要注意)。只接受普通文件更改 unless -d if -f .

† 澄清说,确实,不同目录中可能存在同名文件。这些应复制到相同的名称,并在扩展名之前以序列号为后缀。
为此,我们需要检查名称是否已经存在,并在构建哈希时跟踪重复的名称,因此这将花费更长的时间。那么如何解释重名有一个小难题?我在 arrayrefs 中使用了另一个哈希,其中只保留了重复的名称‡;这简化并加快了工作的两个部分。
my (%fqn, %dupe_names);
find( sub {
return if -d;
(exists $fqn{$_})
? push( @{ $dupe_names{$_} }, $File::Find::name )
: ( $fqn{$_} = $File::Find::name );
}, $source_dir );
令我惊讶的是,在散布在庞大层次结构中的 25 万个文件上,即使现在每个项目都运行测试,它的运行速度也比不考虑重复名称的代码慢一点。
ternary operator 中赋值的括号是必需的,因为可能会将运算符分配给(如果最后两个参数是有效的“左值”,因为它们在这里),因此需要小心分支内的分配。
然后复制后 %fqn与帖子的主要部分一样,也复制其他具有相同名称的文件。我们需要分解文件名,以便在 .ext 之前添加枚举;我用核心 File::Basename
use File::Basename qw(fileparse);

foreach my $fname (@files) {
next if not exists $dupe_names{$fname}; # no dupe (and copied already)
my $cnt = 1;
foreach my $fqn (@{$dupe_names{$fname}}) {
my ($name, $path, $ext) = fileparse($fqn, qr/\.[^.]*/);
copy $fqn, "$copy_to_dir/${name}_$cnt$ext";
or do {
warn "Can't copy $fqn to $copy_to_dir: $!";
next;
};
++$cnt;
}
}
(已完成基本测试,但仅此而已)
我可能会使用 undef而不是 $path上面,表示该路径未使用(同时这也避免了分配和填充标量),但为了让那些不熟悉模块的子返回的人清楚,我将其保留为这种方式。
笔记。对于有重复的文件,会有副本 fname.ext , fname_1.ext等。如果您希望将它们全部编入索引,则首先重命名 fname.ext (在目的地,已通过 %fqn 复制)到 fname_1.ext ,并将计数器初始化更改为 my $cnt = 2; .

‡ 请注意,这些绝不需要是相同的文件。

关于python - 从包含数百万个文件的目录 (bash/python/perl) 中通过精确匹配有效地查找数千个文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61843060/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com