bash - tutorial - shell script命令




删除bash中最新的X文件 (11)

有一个简单的方法,在一个漂亮的标准UNIX环境中用bash运行一个命令来从目录中删除除最新X文件以外的所有文件?

为了给出更具体的例子,设想一些cron作业每隔一小时将一个文件(例如日志文件或tar-up备份)写入一个目录。 我想要一种方法来运行另一个cron作业,它将删除该目录中最旧的文件,直到少于5个。

只是要清楚,只有一个文件存在,它不应该被删除。


thelsdj的答案更简单:

ls -tr | head -n -5 | xargs rm

ls -tr显示所有最早的文件(-t最新的第一个,-r反向)。

head -n -5只显示最后5行(即5个最新文件)。

xargs rm为每个选定的文件调用rm。


删除目录中最近的5个文件(或其他数字)。

rm `ls -t | awk 'NR>5'`

在Debian上运行(假设它与我获得的其他发行版相同:rm:无法删除目录`..'

这很烦人..

无论如何,我调整了上述内容,并在命令中添加了grep。 在我的情况下,我有6个备份文件在目录中,例如file1.tar file2.tar file3.tar等,我想只删除最旧的文件(删除我的情况下的第一个文件)

我跑去删除最旧的文件的脚本是:

ls -C1 -t | grep文件| awk'NR> 5'| xargs rm

这(如上)删除我的第一个文件,例如file1.tar,这也留下了file2 file3 file4 file5和file6


在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

当前目录中有目录时,所有这些答案都会失败。 这是有用的东西:

find . -maxdepth 1 -type f | xargs -x ls -t | awk 'NR>5' | xargs -L1 rm

这个:

  1. 在当前目录中有目录时工作

  2. 尝试删除每个文件,即使前一个文件无法删除(由于权限等原因)

  3. 如果当前目录中的文件数量过多,并且xargs通常会将您拧过( -x

  4. 不符合文件名中的空格(也许你使用的是错误的操作系统?)


忽略新行忽略了安全性和良好的编码。 wnoise有唯一的好答案。 这是他的一个变体,将文件名放在数组$ x中

while IFS= read -rd ''; do 
    x+=("${REPLY#* }"); 
done < <(find . -maxdepth 1 -printf '%[email protected] %p\0' | sort -r -z -n )

我把它做成了一个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

现有答案的问题:

  • 无法处理带嵌入空格或换行符的文件名。
    • 如果解决方案直接在不带引号的命令替换( rm `...` )上调用rm ,那么会增加无意识通配的风险。
  • 无法区分文件和目录(例如,如果目录恰好是最近修改的5个文件系统项目中的一个,则实际上会保留少于 5个文件,并且将rm应用于目录将失败)。

wnoise的回答解决了这些问题,但解决方案是GNU特定的(并且非常复杂)。

这是一个符合POSIX标准的实用解决方案 ,它只提供一个警告 :它不能处理带有嵌入换行符的文件名 - 但我不认为这是大多数人真正关心的问题。

为了记录,下面解释为什么解析ls输出通常不是一个好主意: http://mywiki.wooledge.org/ParsingLs : 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 no --no-run-if-empty )确保在没有输入的情况下不会调用rm

如果你有BSD xargs (包括在OS X上 ),你可以使用-0来处理NUL分离的输入,首先将换行符转换为NUL0x0 )字符,该字符也一次性传递(通常)所有文件名与GNU xargs ):

ls -tp | grep -v '/$' | tail -n +6 | tr '\n' '\0' | xargs -0 rm --

说明:

  • ls -tp打印文件系统项目的名称,按降序排列(按照最近修改的项目的先后顺序排列)(- -t ),目录打印后跟/将它们标记为( -p )。
  • grep -v '/$'然后通过省略具有尾部//$ )的( -v )行来清除结果列表中的目录。
    • 警告 :由于指向目录符号链接在技​​术上本身不是一个目录,因此这种符号链接不会被排除。
  • tail -n +6跳过列表中的前5个条目,实际上只返回5个最近修改的文件(如果有的话)。
    请注意,为了排除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

(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

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

需要针对-printf的GNU查找,针对-z的GNU排序,针对“\ 0”的GNU awk,以及针对-0的GNU xargs,但处理带有嵌入换行符或空格的文件。


ls -tQ | tail -n+4 | xargs rm

按修改时间列出文件名,引用每个文件名。 排除前3(最近3次)。 删除剩余的。

在mklement0的有用评论之后编辑(谢谢!):更正了-n + 3参数,并且注意如果文件名包含换行符和/或目录包含子目录,则这将无法按预期工作。







scripting