删除少于 x 行的文件的快速方法

dur*_*tti 11 bash

在 bash 中删除 x 行以下目录中所有文件的快速且不太复杂的方法是什么?

Wil*_*ard 12

这是一个 POSIX 解决方案,应该很容易理解:

find . -type f -exec awk -v x=10 'NR==x{exit 1}' {} \; -exec echo rm -f {} \;
Run Code Online (Sandbox Code Playgroud)

正如在Stephane 的回答中一样,删除对echo将要删除的内容感到满意的时候。


为那些完全不熟悉 Unix/Linux 的人编写的解释:

.代表当前目录。 find在 中递归查找文件和目录.,并可以对它们进行处理。

-typefind初选之一;这是一个测试,将对递归找到的每个文件和目录(在 内.)执行,并且仅当结果为“true”时才评估该行上的其余主要部分。

在这种特殊情况下,我们只有在处理常规文件时才继续,而不是目录或其他东西(例如块设备)。


-exec主(共find)调用外部命令,并且仅进行到下一个主如果成功的外部命令退出(“0”退出状态)。将{}被替换的文件名被“认为是”被find命令。所以第一次-exec调用相当于下面的shell命令,依次对每个文件执行:

awk -v x=10 'NR==x{exit 1}' ./somefilename
Run Code Online (Sandbox Code Playgroud)

Awk 本身就是一门完整的语言,设计用于处理带分隔符的文本文件,例如 CSV。Awk 条件和命令(包含在单引号之间并以字母开头NR)对文本文件的每一行执行。(隐式循环。)

要全面学习 Awk,我强烈推荐Grymoire 教程,但我将解释上述命令中使用的 Awk 功能。


-vAwk的标志允许我们在执行 Awk 命令之前(针对文件的每一行)设置一个 Awk 变量(一次)。在这种情况下,我们设置x10.


NR是一个特殊awk中变量指的是“ Ñ的电流的棕土- [R的eCord”。换句话说,它是我们在任何特定循环中查看的行号。

(注意,这可能的,但不寻常的,使用不同的“ [R的eCord小号eparator”不是一个换行符的默认情况下,设置RS这是与记录分隔玩的例子。


awk 脚本通常由条件(大括号外)和动作(大括号内)组成。可以有复合条件和复合动作,还有一个默认条件(真)和一个默认动作(打印),但我们需要不用管那些。

这里的条件是,“这是第 10 行吗?” 如果是这种情况,我们会以非零退出状态退出,这在 shell 脚本中意味着“不成功的命令终止”。

因此,此 Awk 命令成功退出的唯一方法是在到达第 10 行之前到达文件末尾。

所以如果 awk 脚本成功退出,说明你的文件少于十行。


下一次-exec调用(如果您删除echo)将find通过运行删除每个文件(在评估的初选时得到那么远):

rm -f ./somefilename
Run Code Online (Sandbox Code Playgroud)


Sté*_*las 5

假设find支持-readable谓词的实现(如果您find不支持它,只需删除它,您只会收到不可读文件的错误消息,或替换为-exec test -r {} \;):

x=10 find . -type f -readable -exec sh -c '
  for file do
    lines=$(wc -l < "$file") && [ "$((lines))" -lt "$x" ] && echo rm -f "$file"
  done' sh {} +
Run Code Online (Sandbox Code Playgroud)

删除echo如果快乐。

这并不是特别有效,因为它计算每个文件中的所有行,而它只需要在x一个处停止wcrm为每个文件运行一个(并且可能是一个)命令。

使用 GNU awk,您可以通过以下方式提高效率:

x=10
find . -type f -readable -exec awk -v x="$x" -v ORS='\0' '
  FNR == x {nextfile}
  ENDFILE {if (FNR < x) print FILENAME}' {} +|
  xargs -r0 echo rm -f
Run Code Online (Sandbox Code Playgroud)

(再次,echo高兴时删除)。

perl以下相同:

x=10 find . -type f -readable -exec perl -Tlne '
  if ($. == $ENV{x}) {close ARGV}
  elsif (eof) {print $ARGV; close ARGV}' {} +
Run Code Online (Sandbox Code Playgroud)

更换printunlink是否幸福。

  • @tomas,最后一个 `sh` 是该内联脚本的 `$0` 中的内容,例如用于错误消息。`wc -l "$file"` 会打印我们不想要的文件名,即使文件无法打开也会运行 `wc`。`$x` 被导出到 `find`(`x=10 find...`),它本身将它传递给 `sh`。 (3认同)