[英]xargs and find, rm complaining about \n (newline) in filename
I am trying to delete the oldest file in a tree with a script in Debian. 我试图用Debian中的脚本删除树中最旧的文件。
find /home/backups -type f \( -name \*.tgz -o -name \*.gz \) -print0 | xargs -0 ls -t | tail -1 | xargs -0 rm
But I am getting an error: 但我收到一个错误:
rm: cannot remove `/home/backups/tree/structure/file.2011-12-08_03-01-01.sql.gz\n': No such file or directory
Any ideas what I am doing wrong (or is there an easier/better way?), I have tried to RTFM, but am lost. 任何想法我做错了(或者有更简单/更好的方法?),我尝试过RTFM,但我迷失了。
The ls
appends a newline and the last xargs -0 says the newline is part of the file name. ls
附加一个换行符,最后一个xargs -0表示换行符是文件名的一部分。 Run the last xargs with -d '\\n'
instead of -0
. 使用
-d '\\n'
而不是-0
运行最后一个xargs。
BTW, due to the way xargs works, your whole pipe is a bug waiting to happen. 顺便说一下,由于xargs的工作方式,你的整个管道都是等待发生的错误。 Consider a really long file name list produced by the
find
, so that the xargs -0 ls
runs ls
multiple times with subsets of the filenames. 考虑一个由
find
生成的非常长的文件名列表,以便xargs -0 ls
使用文件名的子集多次运行ls
。 Only the oldest of the last ls
invocation will make it past the tail -1
. 只有最后一次
ls
调用中最旧的将使它超过tail -1
。 If the oldest file is actually, say, the very first filename output by find
, you are deleting a younger file. 如果最旧的文件实际上是,例如,通过
find
输出的第一个文件名,则删除较年轻的文件。
Any solution involving ls
is absolutely wrong. 任何涉及
ls
解决方案都是绝对错误的
The correct way to do this is to use find
to fetch the set of files, sort
to order them chronologically, filter out all but the first, then rm
to delete. 执行此操作的正确方法是使用
find
来获取文件集, sort
按时间顺序sort
,过滤掉除第一个之外的所有文件,然后删除rm
。 @Ken had this mostly right, missing only a few details. @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")" ; }
Remove the echo
above to actually perform the deletion. 删除上面的
echo
以实际执行删除。
The above code works even for files which have spaces, newlines or other unusual values in the file names. 上述代码甚至适用于文件名中包含空格,换行符或其他异常值的文件。 It will also do nothing harmful when there are no results.
没有结果时,它也不会造成任何伤害。
If you don't care about breaking on newlines in filenames this gets a bit easier 如果你不关心打破文件名中的换行符,这会更容易一些
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
The difference is that we can rely on head
and can use cut
on a pipe instead of doing anything crazy. 不同之处在于我们可以依靠
head
并且可以在管道上使用cut
而不是做任何疯狂的事情。
您还可以使用find随意打印修改时间,排序,剪切和xargs:
find /home/backups -printf "%T@ %p\n" | sort -n | head -1 | cut -d" " -f2- | xargs ls -al
ls
emits newlines as separators, so you need to replace the second xargs -0
with xargs -d '\\n'
. ls
作为分隔符发出换行符,因此您需要将第二个xargs -0
替换为xargs -d '\\n'
。 Breaks, though, if the oldest file has a newline in its name. 但是,如果最旧的文件名称中有换行符,则会中断。
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
from: 在Linux中按日期排序文件列表(包括子目录)
Edit I missed the point of ls -t
there. 编辑我错过了
ls -t
那里的点。
Might I suggest doing it much simpler, eg 我可能会建议这样做更简单,例如
find /home/backups \
-type f -iregex '.*\.t?gz$' \
-mtime +60 -exec rm {} \;
which will delete any matching file older than a specific age (60 days, in the example) 这将删除任何早于特定年龄的匹配文件(在示例中为60天)
You used
tail
but haven't told it to look for null-delimiters.
你使用了
tail
但没有告诉它寻找null-delimiters。
Regardless, here is a util that you could use to return the last 0-delimited element: 无论如何,这是一个可以用来返回最后一个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;
}
Use it as 用它作为
find /home/backups -type f \( -name \*.tgz -o -name \*.gz \) -print0 | ./mytail | xargs -0 rm
ls -tr $(find /home/backups -name '*.gz' -o -name '*.tgz')|head -1|xargs rm -f
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.