在一个非常标准的带有bash的UNIX环境中,有一种简单的方法来运行命令来删除目录中除最新X文件以外的所有文件吗?
为了给出更多具体示例,假设某个cron作业每小时将一个文件(例如,日志文件或tar-up备份)写入目录。我想要一种方法来运行另一个cron作业,它将删除该目录中最旧的文件,直到少于5个。
为了清楚起见,只有一个文件存在,它永远不会被删除。
答案 0 :(得分:92)
删除目录中除最近文件的5个(或任何数量)之外的所有文件。
rm `ls -t | awk 'NR>5'`
答案 1 :(得分:91)
现有答案的问题:
rm
)上调用rm `...`
,则会增加意外通配的风险。rm
应用于目录将失败。wnoise's answer解决了这些问题,但解决方案是 GNU - 特定(并且非常复杂)。
这是一个务实的,符合POSIX标准的解决方案,只有一个警告:它无法处理嵌入了换行符的文件名 - 但我不知道不要认为这是大多数人的现实问题。
为了记录,这里解释为什么解析ls
输出通常不是一个好主意:http://mywiki.wooledge.org/ParsingLs
ls -tp | grep -v '/$' | tail -n +6 | xargs -I {} rm -- {}
以上效率低下,因为xargs
必须为每个文件名调用一次rm
。
您的平台xargs
可能允许您解决此问题:
如果你有 GNU xargs
,请使用-d '\n'
,这会使xargs
将每个输入行视为一个单独的参数,传递与命令行一样多的参数:
ls -tp | grep -v '/$' | tail -n +6 | xargs -d '\n' -r rm --
-r
(--no-run-if-empty
)确保在没有输入的情况下不会调用rm
。
如果您有 BSD xargs
(包括 OS X ),您可以使用-0
来处理NUL
- 首次将换行符转换为NUL
(0x0
)个字符后的分隔输入,这些字符也会(通常)同时传递所有文件名 (也可以使用GNU xargs
):
ls -tp | grep -v '/$' | tail -n +6 | tr '\n' '\0' | xargs -0 rm --
<强>解释强>
ls -tp
打印文件系统项目的名称,按照最近修改的方式排序,按降序排列(最近修改的项目)(-t
),目录打印为尾随{{1}将它们标记为(/
)。-p
然后通过省略(grep -v '/$'
)具有尾随-v
(/
)的行来清除结果列表中的目录。
/$
会跳过列表中的第一个 5 条目,实际上会返回所有但最近修改的5个文件(如果有的话)。tail -n +6
个文件,必须将N
传递给N+1
。tail -n +
(及其变体)然后在xargs -I {} rm -- {}
上调用所有这些文件;如果根本没有匹配,rm
将不会做任何事情。
xargs
定义占位符xargs -I {} rm -- {}
,它代表每个输入行作为一个整体,因此{}
会为每个输入行调用一次,但文件名为嵌入空间处理正确。rm
在所有情况下都会确保--
以-
开头的所有文件名都不会被rm
误认为是选项。原始问题的变体,以防需要处理匹配文件单独或收集在shell数组中 强>:
# One by one, in a shell loop (POSIX-compliant):
ls -tp | grep -v '/$' | tail -n +6 | while IFS= read -r f; do echo "$f"; done
# One by one, but using a Bash process substitution (<(...),
# so that the variables inside the `while` loop remain in scope:
while IFS= read -r f; do echo "$f"; done < <(ls -tp | grep -v '/$' | tail -n +6)
# Collecting the matches in a Bash *array*:
IFS=$'\n' read -d '' -ra files < <(ls -tp | grep -v '/$' | tail -n +6)
printf '%s\n' "${files[@]}" # print array elements
答案 2 :(得分:85)
(ls -t|head -n 5;ls)|sort|uniq -u|xargs rm
此版本支持带空格的名称:
(ls -t|head -n 5;ls)|sort|uniq -u|sed -e 's,.*,"&",g'|xargs rm
答案 3 :(得分:58)
thelsdj答案的简单变体:
ls -tr | head -n -5 | xargs --no-run-if-empty rm
ls -tr显示所有文件,最早的文件(-t最新的第一个,-r反向)。
head -n -5显示除最后5行之外的所有行(即5个最新文件)。
xargs rm为每个选定的文件调用rm。
答案 4 :(得分:16)
find . -maxdepth 1 -type f -printf '%T@ %p\0' | sort -r -z -n | awk 'BEGIN { RS="\0"; ORS="\0"; FS="" } NR > 5 { sub("^[0-9]*(.[0-9]*)? ", ""); print }' | xargs -0 rm -f
需要GNU find for -printf,GNU sort for -z,GNU awk for“\ 0”,GNU xargs for -0,但处理带有嵌入换行符或空格的文件。
答案 5 :(得分:13)
当前目录中有目录时,所有这些答案都会失败。这是有用的:
find . -maxdepth 1 -type f | xargs -x ls -t | awk 'NR>5' | xargs -L1 rm
此:
在当前目录中有目录时有效
尝试删除每个文件,即使前一个文件无法删除(由于权限等)
当前目录中的文件数量过多而xargs
通常会让您失意(-x
)
不满足文件名中的空格(也许你使用的是错误的操作系统?)
答案 6 :(得分:12)
ls -tQ | tail -n+4 | xargs rm
按修改时间列出文件名,引用每个文件名。排除前3位(最近3位)。删除剩余的。
在mklement0的有用评论后编辑(谢谢!):更正了-n + 3参数,并注意如果文件名包含换行符和/或目录包含子目录,这将无法正常工作。
答案 7 :(得分:8)
忽略换行符会忽略安全性和良好的编码。 wnoise有唯一的好答案。这是他的一个变体,它将文件名放在数组$ x
中while IFS= read -rd ''; do
x+=("${REPLY#* }");
done < <(find . -maxdepth 1 -printf '%T@ %p\0' | sort -r -z -n )
答案 8 :(得分:4)
如果文件名没有空格,则可以使用:
ls -C1 -t| awk 'NR>5'|xargs rm
如果文件名确实有空格,比如
ls -C1 -t | awk 'NR>5' | sed -e "s/^/rm '/" -e "s/$/'/" | sh
基本逻辑:
答案 9 :(得分:2)
使用zsh
假设您不关心当前目录,并且您将不会有超过999个文件(如果需要,请选择更大的数字,或者创建一个while循环)。
[ 6 -le `ls *(.)|wc -l` ] && rm *(.om[6,999])
在*(.om[6,999])
中,.
表示文件,o
表示排序顺序,m
表示按修改日期(表示a
表示访问权限对于inode更改的时间或c
,[6,999]
选择一个文件范围,因此不会首先记录5。
答案 10 :(得分:2)
我意识到这是一个老线程,但也许有人会从中受益。此命令将在当前目录中找到文件:
for F in $(find . -maxdepth 1 -type f -name "*_srv_logs_*.tar.gz" -printf '%T@ %p\n' | sort -r -z -n | tail -n+5 | awk '{ print $2; }'); do rm $F; done
这比以前的一些答案要强一些,因为它允许将搜索域限制为匹配表达式的文件。首先,找到符合您想要的任何条件的文件。打印那些带有时间戳的文件。
find . -maxdepth 1 -type f -name "*_srv_logs_*.tar.gz" -printf '%T@ %p\n'
接下来,按时间戳排序:
sort -r -z -n
然后,从列表中删除最近的4个文件:
tail -n+5
抓住第二列(文件名,而不是时间戳):
awk '{ print $2; }'
然后将整个内容包装成for语句:
for F in $(); do rm $F; done
这可能是一个更详细的命令,但我有更好的运气能够针对条件文件并执行更复杂的命令。
答案 11 :(得分:1)
在Sed-Onliners中找到了有趣的cmd - 删除了最后3行 - 这对于另一种皮肤猫的方式来说是完美的(好吧没有)但想法:
#!/bin/bash
# sed cmd chng #2 to value file wish to retain
cd /opt/depot
ls -1 MyMintFiles*.zip > BigList
sed -n -e :a -e '1,2!{P;N;D;};N;ba' BigList > DeList
for i in `cat DeList`
do
echo "Deleted $i"
rm -f $i
#echo "File(s) gonzo "
#read junk
done
exit 0
答案 12 :(得分:1)
删除除10个最新(最近的)文件之外的所有文件
ls -t1 | head -n $(echo $(ls -1 | wc -l) - 10 | bc) | xargs rm
如果少于10个文件,则不删除任何文件,您将拥有: 错误头:非法行数 - 0
答案 13 :(得分:1)
我需要一个用于busybox(路由器)的优雅解决方案,所有xargs或阵列解决方案对我来说都是无用的-那里没有这样的命令。 find和mtime不是正确的答案,因为我们谈论的是10个项目,不一定是10天。埃斯波的回答是最短,最简洁,也可能是最普遍的回答。
空格错误和什么都不删除文件都可以通过标准方式解决:
rm "$(ls -td *.tar | awk 'NR>7')" 2>&-
更具教育意义的版本:如果我们以不同的方式使用awk,我们可以做到。通常,我使用这种方法将变量从awk传递(返回)到sh。当我们阅读所有无法完成的时间时,我想请您有所不同:这是方法。
.tar文件示例,文件名中的空格没有问题。要进行测试,请将“ rm”替换为“ ls”。
eval $(ls -td *.tar | awk 'NR>7 { print "rm \"" $0 "\""}')
说明:
ls -td *.tar
列出了按时间排序的所有.tar文件。要应用当前文件夹中的所有文件,请删除“ d * .tar”部分
awk 'NR>7...
跳过前7行
print "rm \"" $0 "\""
构造一行:rm“文件名”
eval
执行它
由于我们使用的是rm
,所以我不会在脚本中使用以上命令! Wiser用法是:
(cd /FolderToDeleteWithin && eval $(ls -td *.tar | awk 'NR>7 { print "rm \"" $0 "\""}'))
在使用ls -t
命令的情况下,不会对诸如touch 'foo " bar'
和touch 'hello * world'
这样的愚蠢示例造成任何损害。并不是说我们曾经在现实生活中使用这样的名称创建文件!
侧注。如果我们想通过这种方式将变量传递给sh,我们只需修改打印内容(简单形式,不能容忍空格):
print "VarName="$1
将变量VarName
设置为$1
的值。可以一次创建多个变量。该VarName
成为普通的sh变量,之后可以在脚本或shell中正常使用。因此,要使用awk创建变量并将其返回给Shell:
eval $(ls -td *.tar | awk 'NR>7 { print "VarName=\""$1"\"" }'); echo "$VarName"
答案 14 :(得分:0)
leaveCount=5
fileCount=$(ls -1 *.log | wc -l)
tailCount=$((fileCount - leaveCount))
# avoid negative tail argument
[[ $tailCount < 0 ]] && tailCount=0
ls -t *.log | tail -$tailCount | xargs rm -f
答案 15 :(得分:0)
我把它变成了一个bash shell脚本。用法:keep NUM DIR
其中NUM是要保留的文件数,DIR是要擦除的目录。
#!/bin/bash
# Keep last N files by date.
# Usage: keep NUMBER DIRECTORY
echo ""
if [ $# -lt 2 ]; then
echo "Usage: $0 NUMFILES DIR"
echo "Keep last N newest files."
exit 1
fi
if [ ! -e $2 ]; then
echo "ERROR: directory '$1' does not exist"
exit 1
fi
if [ ! -d $2 ]; then
echo "ERROR: '$1' is not a directory"
exit 1
fi
pushd $2 > /dev/null
ls -tp | grep -v '/' | tail -n +"$1" | xargs -I {} rm -- {}
popd > /dev/null
echo "Done. Kept $1 most recent files in $2."
ls $2|wc -l
答案 16 :(得分:-4)
在Debian上运行(假设我得到的其他发行版也一样: rm:无法删除目录`..'
这很烦人..
无论如何,我调整了上面的内容,并在命令中添加了grep。在我的情况下,我在目录中有6个备份文件,例如file1.tar file2.tar file3.tar等我想删除最旧的文件(在我的情况下删除第一个文件)
我运行的删除最旧文件的脚本是:
ls -C1 -t | grep文件| awk'NR&gt; 5'| xargs rm
这(如上所述)删除了我的第一个文件,例如file1.tar这也留下了file2 file3 file4 file5和file6