xargs和发现,rm抱怨文件名中的\ n(换行符)

我想用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,但我迷路了。

Solutions Collecting From Web of "xargs和发现,rm抱怨文件名中的\ n(换行符)"

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

顺便说一句,由于xargs的工作方式,你的整个管道是一个等待发生的错误。 考虑一个由find生成的非常长的文件名列表,这样xargs -0 ls ls多次使用文件名的子集运行ls 。 只有最后一个ls调用中的最老的才能使它通过tail -1 。 如果最老的文件实际上是find第一个文件名输出,那么你正在删除一个更年轻的文件。

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

这样做的正确方法是使用find来获取文件集, sort时间顺序对它们进行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管,而不是做任何疯狂的事情。

你也可以使用find来打印出修改时间,排序,剪切和xargs:

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

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

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

编辑我错过了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 
 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 

来自: 在Linux中按日期排序文件列表(包括子目录)