xargs和find,rm在文件名中抱怨\ n(换行符)

时间:2011-12-15 11:17:39

标签: linux bash xargs

我正在尝试使用Debian中的脚本删除树中最旧的文件。

find /home/backups -type f \( -name \*.tgz -o -name \*.gz \) -print0 | xargs -0 ls -t | tail -1 | xargs -0 rm

但是我收到了一个错误:

rm: cannot remove `/home/backups/tree/structure/file.2011-12-08_03-01-01.sql.gz\n': No such file or directory

任何想法我做错了(或者有更简单/更好的方法吗?),我尝试过RTFM,但我迷路了。

7 个答案:

答案 0 :(得分:11)

ls附加换行符,最后一个xargs -0表示换行符是文件名的一部分。 使用-d '\n'而不是-0运行最后一个xargs。

顺便说一句,由于xargs的工作方式,你的整个管道都是等待发生的错误。考虑find生成的一个非常长的文件名列表,以便xargs -0 ls使用文件名的子集多次运行ls。只有最早的最后ls次调用会使其超过tail -1。如果最早的文件实际上是,例如find输出的第一个文件名,则删除较年轻的文件。

答案 1 :(得分:3)

任何涉及ls的解决方案都是绝对错误的。

执行此操作的正确方法是使用find获取文件集sort按时间顺序排序,过滤掉除第一个之外的所有文件,然后删除rm。 @Ken这个大概是正确的,只缺少一些细节。

find /home/backups -type f \( -name \*.tgz -o -name \*.gz \) -printf '%T@ %p\0' |\
    sort -z -n | \
    { IFS= read -d '' file ; [ -n "$file" ] && echo rm -f "$(cut -d' ' -f2- <<<"$file")" ; }

删除上面的echo以实际执行删除。

上述代码甚至适用于文件名中包含空格,换行符或其他异常值的文件。当没有结果时,它也不会造成任何伤害。

如果你不关心打破文件名中的换行符,这会更容易

find /home/backups -type f \( -name \*.tgz -o -name \*.gz \) -printf '%T@ %p\n' |\
    sort -n |\
    head -n 1 |\
    cut -d' ' -f2- |\
    xargs echo rm

不同之处在于我们可以依赖head并且可以在管道上使用cut而不是做任何疯狂的事情。

答案 2 :(得分:2)

您还可以使用find随意打印修改时间,排序,剪切和xargs:

find /home/backups -printf "%T@ %p\n" | sort -n | head -1 | cut -d" " -f2- | xargs ls -al

答案 3 :(得分:1)

ls以分隔符的形式发出换行符,因此您需要将第二个xargs -0替换为xargs -d '\n'。但是,如果最旧的文件名称中有换行符,则会中断。

答案 4 :(得分:0)

修改我错过了那里的ls -t

我可能会建议这样做更简单,例如。

find /home/backups \
    -type f -iregex '.*\.t?gz$' \
    -mtime +60 -exec rm {} \;

将删除任何早于特定年龄的匹配文件(示例中为60天)


您使用了tail,但没有告诉它寻找空分隔符。

无论如何,这是一个可用于返回最后一个0分隔元素的util:

#include <string>
#include <iostream>
#include <cstdio>

int main(int argc, const char *argv[])
{
    std::cin.unsetf(std::ios::skipws);
    if (!  (freopen(NULL, "wb", stdout) && freopen(NULL, "rb", stdin) ))
    {
        perror("Cannot open stdout/in in binary mode");
        return 255;
    }

    std::string previous, element;
    while (std::getline(std::cin, element, '\0'))
    {
        previous = element; 
        // if you have c++0x support, use this _instead_ for performance:
        previous = std::move(element);
    }

    std::cout << previous << '\0' << std::flush;
}

将其用作

find /home/backups -type f \( -name \*.tgz -o -name \*.gz \) -print0 | ./mytail | xargs -0 rm 

答案 5 :(得分:0)

ls -tr $(find /home/backups -name '*.gz' -o -name '*.tgz')|head -1|xargs rm -f

答案 6 :(得分:0)

find /home/backups -type f \( -name \*.tgz -o -name \*.gz \) -print0 | xargs -0 stat --format '%010Y:%n' | sort -n | head -n 1 | cut -d: -f2- | xargs -d '\n' rm 

来自:Sort a file list by Date in Linux (Including Subdirectories)