我有一个包含子目录的目录,我想从中删除名称中包含out
的所有文件。这样做的最快方法是什么?
我尝试过几件事。
简单:
rm */*out*
的Perl:
perl -e 'for ( <*/*out*> ) { ( (stat)[9] < (unlink) ) }'
每一项似乎都需要花费大量时间。对于1,000个子目录,每个子目录包含大约50个与*out*
匹配的文件,需要:
Perl: ~25 mins
rm */*out* : ~18 mins
我还尝试rsync
,首先将文件移动到文件夹然后与删除同步,但这需要很长时间。
有没有人有更快的方法摆脱这些文件,因为这对我来说似乎过于缓慢?
答案 0 :(得分:4)
我发现test3
是最快的(11-25秒)。但为什么不亲自测试呢?
您的文件系统会对性能产生重大影响。
测试使用GNU Parallel。
# Make test set: 150000 files, 50000 named *.seq
testset() {
doit() { mkdir -p $1 ; cd $1 && parallel --results ./{} seq ::: {1..50}; }
export -f doit
seq 1000 | parallel --bar doit >/dev/null
# Drop caches before starting a test
echo 3 | sudo tee /proc/sys/vm/drop_caches >/dev/null
}
export -f testset
# Define tests
test1() {
find . -name '*seq' | perl -ne 'chop;unlink'
}
export -f test1
test2() {
find . -name '*seq' -delete
}
export -f test2
test3() {
find . -name '*seq' | parallel --pipe -N1000 -q perl -ne 'chop;unlink'
}
export -f test3
test4() {
find . -name '*seq' -print0 | xargs -0 -P2 rm
}
export -f test4
test5() {
find . -name '*seq' -print0 | xargs -0 rm
}
export -f test5
test6() {
find . -name '*seq' | perl -e 'chomp(@a=<>);unlink @a'
}
export -f test6
test7() {
# sort by inode
ls -U -i */*seq* | sort -k1,1 -n| cut -d' ' -f2- | perl -e 'chomp(@a=<>);unlink @a'
}
export -f test7
# Run testset/test? alternating
eval parallel --joblog jl -uj1 ::: testset' 'test{1..7}
# sort by runtime
sort -nk4 jl
答案 1 :(得分:1)
我曾经遇到过一个类似的问题,一个工具变得狂暴,并在一段时间后留下了400.000个临时文件。
我使用rm *
,find . -name ... -exec rm {} +
和一些Perl解决方案进行了很多实验。
令我惊讶的是,这是迄今为止最快的方法:
unlink @list
示例:
if ( my $dh = IO::Dir->new($dir) ) {
my @files_to_delete = ();
while ( my $file = $dh->read() ) {
$file = "$dir/$file";
if ( -f $file ) {
push @files_to_delete, $file;
}
}
$dh->close();
my $deleted = unlink @files_to_delete;
print "deleted $deleted files\n";
}
还有其他方法可以确定@files_to_delete
(例如glob
等),但关键点是unlink @files_to_delete
步骤。
立即使用尽可能多的文件调用unlink
。文件数量似乎没有限制(内存除外)。
这实际上让我感到惊讶,因为我rm *
(或其等价物)会比Perl unlink
更快 - 但它不是。
答案 2 :(得分:1)
你的命令似乎确实执行得异常缓慢。
至于您尝试的内容:
rm */*out*
:
getconf ARG_MAX
报告,因为模式(glob)*/*out*
由shell 预先扩展,以及生成的文件名列表会立即传递给外部实用程序rm
。我假设您意味着使用的perl
命令是perl -e 'unlink <*/*out*>'
:
rm */*out*
,但YMMV(见下文);它确实具有不受getconf ARG_MAX
限制的明显优势;但是,随着输入集变大,它确实会变慢 - 见下文。使用并行执行可以帮助:
GNU和BSD / macOS xargs
具有非标准-P <n>
选项,允许并行运行指定命令的大多数<n>
个实例; GNU xargs
支持0
<n>
,这意味着&#34; xargs将运行尽可能多的进程&#34;,根据{{1}页面;虽然细节不清楚,并且它也说明了,&#34;使用man
选项或-n
选项-L
;否则很可能只有一名执行官会完成&#34;在实践中它似乎确实有所作为。
-P
是我测试中最快的 - YMMV。GNU Parallel - 通常不预装 - 值得考虑一般的并行执行的复杂控制,尽管可能在这种特殊情况下没有帮助;它是一个强大的工具,但不是灵丹妙药 - YMMV。
下面是一个find . -name '*out*' -print0 | xargs -0 -P0 rm
脚本,用于统计各种命令;默认情况下,它使用以下参数:
脚本自行清理并且没有bash
以外的先决条件(例如,您也可以在macOS上运行它);那些假设存在GNU bash
的测试如果不存在则会失败
您可以通过从命令行传递参数或修改默认值,以及通过添加/删除命令来轻松调整脚本。
以下是来自在macOS 10.12.4主机上运行Ubuntu 16.04的双核VMware Fusion VM的 示例时序,这是2012年底 - 4月4日配备Fusion Drive(混合SSD + HHD)的核心Intel i5 3.2 Ghz机器。
再次:YMMV - 有很多因素在起作用,例如文件系统,硬盘驱动器,CPU核心数,系统负载......
parallel
将find . -name '*out*' -print0 | xargs -0 -P0 rm 0.704
find . -name '*out*' -print0 | xargs -0 -P2 rm 0.745
find . -name '*out*' -print0 | xargs -0 rm 1.067
find . -name '*out*' | perl -e 'chomp(@a=<>); unlink @a' 1.070
perl -e 'unlink <*/*out*>' 1.089
find . -name '*out*' -delete 1.094
find . -mindepth 2 -maxdepth 2 -name '*out*' -delete 1.110
rm */*out* 1.369
find . -name '*out*' | parallel --pipe -n 1000 perl -nle 'unlink' 1.460
find . -name '*out*' | parallel --pipe -n 1000 -q perl -e 'chomp(@a=<>); unlink @a' 1.493
find . -name '*out*' | parallel --xargs rm 2.294
find . -name '*out*' | parallel -n 1000 rm 2.465
与find
结合起来似乎是胜利者,即使没有并行调用的xargs -P0
也很快。
也许令人惊讶的是,单一实用程序解决方案(xargs
,rm
)不最快,随着输入集的大小增加,并行解决方案变得相对更快
使用GNU perl
在这里没有帮助,但它可能包含更大的数字。
这是测试脚本的源代码,其唯一先决条件是Bash v3 +;缺少/不同的GNU parallel
和/或超出限制的parallel
命令行的大小将在结果中单独注明。
rm
答案 3 :(得分:0)
使用单个rm删除所有文件(使用find
):
find /path/to/dir -name "*out*" -exec rm {} +
答案 4 :(得分:-1)
怎么样:
find . -mindepth 2 -maxdepth 2 -name '*out*' -delete
使用find . -mindepth 2 -maxdepth 2
,您可以获得.
内深度为2的所有元素,这意味着当前目录(.
)的子目录中的每个文件。在这种情况下,“文件”可以是任何内容:常规文件,目录,管道等。如果您只想删除常规文件,可以添加-type f
选项:
find . -mindepth 2 -maxdepth 2 -name '*out*' -type f -delete
使用-name '*out*'
,您将获得名称包含out
的所有文件。
-delete
告诉find
删除找到的每个文件。请注意,-delete
的所有版本均不支持find
,但您的find
很可能已经支持{{1}}。