【问题标题】:Delete empty files - Improve performance of logic删除空文件 - 提高逻辑性能
【发布时间】:2022-01-21 10:17:18
【问题描述】:

我需要查找并删除空文件。在我的用例中,空文件的定义是一个零行的文件。

我确实尝试测试文件以查看它是否为空但是,这表现得很奇怪,因为即使文件为空它也没有检测到它。

因此,我能写出的最好的东西是下面的脚本,因为它必须测试几十万个文件,所以我太慢了

#!/bin/bash

LOOKUP_DIR="/path/to/source/directory"

cd ${LOOKUP_DIR} || { echo "cd failed"; exit 0; }

for fname in $(realpath */*)
do
        if [[ $(wc -l "${fname}" | awk '{print $1}') -eq 0 ]]
        then
                echo "${fname}" is empty
                rm -f "${fname}"
        fi
done

有没有更好的方法来做我所追求的,或者,能否以带来更好性能的方式重写上述逻辑?

【问题讨论】:

  • find /root/directory -size 0c -delete?
  • @Shawn 这是最好的解决方案,但不是 100% 相同。文件可以有零行 (wc -l = 0),但可以多于 0c
  • @steffen。如何?你能想出一个例子吗?
  • 空的定义也很奇怪。
  • grep -Pzq '\n' "$fname" && echo "line(s)" || echo "zero lines"

标签: bash shell


【解决方案1】:

您的脚本很慢,因为wc 会读取每个文件到最后,这对于您的目的来说是不需要的。这可能是您正在寻找的:

#!/bin/bash

lookup_dir='/path/to/source/directory'

cd "$lookup_dir" || exit
for file in *; do
    if [[ -f "$file" && -r "$file" && ! -L "$file" ]]; then
        read < "$file" || echo rm -f -- "$file"
    fi
done

在确保 echo 按预期工作后删除它。

另一个版本,只调用一次rm,可能是:

#!/bin/bash

lookup_dir='/path/to/source/directory'

cd "$lookup_dir" || exit
for file in *; do
    if [[ -f "$file" && -r "$file" && ! -L "$file" ]]; then
        read < "$file" || files_to_be_deleted+=("$file")
    fi
done
rm -f -- "${files_to_be_deleted[@]}"

说明:
核心逻辑就行了

read < "$file" || rm -f -- "$file"

read &lt; "$file" 命令尝试从$file 中读取一行。如果成功,即读取了一行,那么||右侧的rm命令将不会被执行(||就是这样工作的)。如果失败,则将执行rm 命令。在任何情况下,最多会读取一行。这比wc 命令有很大的优势,因为wc 会读取整个文件。

if ! read < "$file"; then rm -f -- "$file"; fi

可以改用。这两行是等价的。

【讨论】:

  • 好收获;真的需要测试吗?沉默readstderr 似乎足够了
  • @Fravadona 我什至添加了另一个测试 (-r "$file")。我不知道OP是否要rm非常规文件或没有读取权限的文件,所以我认为这种方式更安全。
  • 哇,这在 7 秒内对 600,000 个奇怪的文件执行,而我的脚本过去需要大约 20 分钟或更长时间,具体取决于系统的繁忙程度。非常感谢。
  • @M.NejatAydin - 您能否澄清一下我们在逻辑中的哪个位置检查文件是否为空(其中没有行)
  • 只有一件事需要注意:如果你偶然发现一个没有换行符的大文件,你最终可能会将整个文件加载到内存中
【解决方案2】:

另一种方法

wc -l ~/tmp/* 2>/dev/null | awk '$1 == 0 {print $2}' | xargs echo rm

如果您的任何文件的名称中包含空格,这将中断。

要解决这个问题,仍然使用 awk

wc -l ~/tmp/* 2>/dev/null \
| awk 'sub(/^[[:blank:]]+0[[:blank:]]+/, "")' \
| xargs echo rm

之所以有效,是因为sub 函数返回 的替换次数,可以将其视为布尔零/非零条件。

移除回显以实际删除文件。

【讨论】:

  • 非常感谢格伦。感谢你的帮助。漂亮的一个班轮
【解决方案3】:

要检查“$fname”是否为空文件,请使用[ -s "$fname" ]

#!/usr/bin/env sh

LOOKUP_DIR="/path/to/source/directory"

for fname in "$LOOKUP_DIR"*/*; do
  if ! [ -s "$fname" ]; then
    echo "${fname}" is empty
    # remove echo when output is what you want
    echo rm -f "${fname}"
  fi
done

见:help test

文件操作符:

...

-s FILE 如果文件存在且不为空,则为真。

【讨论】:

  • 非常感谢莉亚。感谢您的帮助
猜你喜欢
  • 2016-08-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-13
  • 2021-09-03
  • 1970-01-01
相关资源
最近更新 更多