【问题标题】:xargs and find, rm complaining about \n (newline) in filenamexargs 和 find, rm 抱怨文件名中的 \n (换行符)
【发布时间】:2012-10-05 20:20:54
【问题描述】:

我正在尝试使用 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,但迷路了。

【问题讨论】:

  • 如果在最后一个xargs 中省略-0 会发生什么?

标签: linux bash xargs


【解决方案1】:

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

顺便说一句,由于 xargs 的工作方式,您的整个管道都是等待发生的错误。考虑由find 生成的非常长的文件名列表,因此xargs -0 ls 使用文件名的子集多次运行ls。只有最后一次ls 调用中最旧的一次会超过tail -1。如果最旧的文件实际上是 find 输出的第一个文件名,那么您正在删除一个较新的文件。

【讨论】:

  • 这还不够。如果没有-d '\n'xargs 在每个空格上都会拆分,这可能不是您想要的。
  • 完美,谢谢。顺便问一下,什么是“非常长的文件名列表”?
  • @user1076412:在这种情况下,“long”表示“大于 ARG_MAX”,这是您的操作系统将设置的值。如果find 返回的所有文件名加起来都比 ARG_MAX 长,那么 xargs 将多次调用它。
  • "long" 表示超过 xargs 对单个命令调用施加的限制。这可能在 ARG_MAX 附近,但也可能是一个低得多的常数,编译成 xargs。请注意,xargs 不能调用具有任意长 arg 列表的命令;它的目的是在超出限制时将 arg 列表拆分为多个命令调用。这对于像rm 这样的命令没有问题,其中多次调用不会改变结果。但在您的情况下, 结果发生了变化。
  • @glglgl: 添加-d '\n' 确实删除了换行符rm 仍然抱怨找不到文件。我通过管道到script 解决了这个问题,但仍然想知道是什么原因造成的。充分意识到这可能会因为缺乏细节而变得困难。
【解决方案2】:

任何涉及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,而不是做任何疯狂的事情。

【讨论】:

    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

      你也可以用find随意打印出修改时间、排序、剪切和xargs:

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

      【讨论】:

      • 这是最好的方法,但我会 cut 使用分隔符而不是依赖固定偏移量。
      • 你可以从我冰冷、死气沉沉的手中夺取我的“svn status | cut -c8-”!但是是的,分隔符会更好
      • 您的编辑引入了一个新错误:如果文件名中有空格,则应显示-f2-
      【解决方案5】:

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

      【讨论】:

      • 这会破坏任何空白和过长的文件列表。
      【解决方案6】:

      编辑我错过了ls -t 的要点。

      我可以建议做的更简单,例如

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

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


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

      无论如何,这里有一个工具可以用来返回最后一个以 0 分隔的元素:

      #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 
      

      【讨论】:

      • 请记住,第一个 xargs 吞下了空分隔符,ls 用换行符分隔,所以尾部没问题。
      • 好主意,但对于这个问题,缺少按时间排序(来自 OP 的 ls -t)。
      • @thiton:shute。我经常想知道为什么 find 没有排序功能。
      • @thiton:再一次调整了我的答案。首先,我真的会尽量避免复杂性
      【解决方案7】:
      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)

      【讨论】:

        猜你喜欢
        • 2017-08-07
        • 1970-01-01
        • 2021-12-26
        • 2011-02-08
        • 2023-01-16
        • 2011-08-18
        • 2020-08-22
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多