【问题标题】:List file using ls to find meet the condition使用ls查找满足条件的list文件
【发布时间】:2019-01-30 13:45:06
【问题描述】:

我正在编写一个批处理程序来删除目录中的所有文件,文件名中有条件。

目录中有大量文本文件(约数十万个文件),文件名固定为“abc”+日期

abc_20180820.txt
abc_20180821.txt
abc_20180822.txt
abc_20180823.txt
abc_20180824.txt

程序尝试grep所有文件,将日期与固定日期进行比较,如果文件名的日期

我的问题:有没有办法在运行 ls 命令时比较日期?不获取列表中的所有文件然后比较删除,而是仅列出已经满足条件的文件然后删除。我认为这样会有更好的表现。

我的代码是

   TARGET_DATE =  "5-12"
    DEL_DATE = "20180823"   
    ls -t |  grep "[0-9]\{8\}".txt\$ > ${LIST}
        for EACH_FILE in  `cat ${LIST}` ;
        do
            DATE=`echo ${EACH_FILE} | cut -c${TARGET_DATE }`
            COMPARE=`expr "${DATE}" \< "${DEL_DATE}"`
            if [ $COMPARE -eq 1 ] ;
            then
                rm -f ${EACH_FILE}
            fi
        done 

发现了一些类似的问题,但我不知道如何完成 List file using ls with a condition and process/grep files that only whitespaces

【问题讨论】:

  • 它必须是您使用的文件名还是您可以稍微模糊一点并使用文件的修改、访问或更改时间?如果是这样,您可以将find 与delete 选项一起使用。 find . -type f -name '*.txt' -mtime +30 -print -delete 之类的东西,其中 +30 是 30 天或更早
  • 不幸的是,find 命令仍然需要相当长的时间。无论如何,感谢您的解决方案。

标签: linux bash shell


【解决方案1】:

这是一个重构,它摆脱了讨厌的ls。循环遍历一个大目录仍然会有点慢。

# Use lowercase for private variables
# to avoid clobbering a reserved system variable

# You can't have spaces around the equals sign
del_date="20180823"   

# No need for ls here
# No need for a temporary file
for filename in *[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9].txt
do
    # Avoid external process; use the shell's parameter substitution
    date=${filename%.txt}
    # This could fail if the file name contains literal shell metacharacters!
    date=${date#${date%?????????}}
    # Avoid expr
    if [ "$date" -lt "$del_date" ]; then
        # Just print the file name, null-terminated for xargs
        printf '%s\0' "$filename"
    fi
done |
# For efficiency, do batch delete
xargs -r0 rm

通配符扩展仍然需要相当长的时间,因为 shell 将对文件名列表进行排序。更好的解决方案可能是将其重构为避免排序的find 命令。

find . -maxdepth 1 -type f \( \
      -name '*1[89][0-9][0-9][0-9][0-9][0-9][0-9].txt' \
   -o -name '*201[0-7][0-9][0-9][0-9][0-9].txt' \
   -o -name '*20180[1-7][0-9][0-9].txt ' \
   -o -name '*201808[01][0-9].txt' \
   -o -name '*2018082[0-2].txt' \
 \) -delete

【讨论】:

  • 循环是它花费大量时间的原因。我用循环尝试了一切。使用find 命令的解决方案更好但相当复杂。我真的很感激。
【解决方案2】:

你可以这样做:

rm 201[0-7]*.txt            # remove all files from 2010-2017
rm 20180[1-4]*.txt          # remove all files from Jan-Apr 2018
# And so on
...

删除大量文件。然后你的代码会运行得更快。

【讨论】:

  • 处理 ~20k 文件非常好,但对于 ~200k 文件[参数列表太长]。顺便说一句,很棒的代码。
  • 好的,你能不能微调一下必须处理太多文件的命令。例如。你能用rm 201801*.txt; rm 201802*.txt; ...代替rm 20180[1-4]*.txt吗
  • 也许你忘记了条件,但原始命令可以删除文件直到删除日期,而不是那种 rm 201801*.txt
  • 不确定你的意思。你能举个例子说明什么是行不通的。无论如何,您说您有大约 200K 文件。它们的文件名在某些方面有所不同。例如。如果所有 200K 文件都是在 2018 年 5 月创建的,那么您可以删除所有在 5 月 1 日创建的文件,然后是 5 月 2 日,依此类推。这将逐渐减少您拥有的文件数量。
  • 我的意思是我想删除文件名中日期小于/在确切给定日期之前的所有文件。所以rm 201801*.txt; rm 201802*.txt 只能在确切的日/月/年工作
【解决方案3】:

是的,如果您在一个文件夹中有这么多文件,这将花费很多时间。 将这么多文件保存在一个文件夹中是个坏主意。即使是简单的 ls 或 find 也会杀死存储。而且,如果您有一些迭代文件的脚本,那么您肯定会杀死存储。 因此,在您等待一小时后进行清洁。花时间制作更好的文件夹结构。根据年/月/日对文件进行排序是个好主意......可能是几个小时 例如

somefolder/2018/08/24/...files here

然后您可以轻松删除、移动压缩...整月或整年。

【讨论】:

  • 感谢您的解决方案,但我必须处理大量数据进行测试,实际数据可能不会发生。
【解决方案4】:

我在这个帖子中找到了解决方案。 https://unix.stackexchange.com/questions/199554/get-files-with-a-name-containing-a-date-value-less-than-or-equal-to-a-given-inpu

awk 命令非常强大,我只需要大约 1 分钟就可以处理数十万个文件(与循环相比是循环的 1/10)。

ls | awk -v date="$DEL_DATE" '$0 <= date' | xargs rm -vrf

我什至可以用我见过的最快答案的命令计算、复制、移动。

COUNT="$(ls | awk -v date="${DEL_DATE}" '$0 <= target' | xargs rm -vrf | wc -l)" 

【讨论】:

    猜你喜欢
    • 2019-01-22
    • 2021-02-01
    • 2013-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-23
    • 2019-11-11
    • 2021-05-15
    相关资源
    最近更新 更多