有没有一种简单的方法,在带有 bash 的非常标准的 UNIX 环境中,运行命令从目录中删除除最新 X 文件之外的所有文件?
举一个更具体的例子,想象一下某个 cron 作业每小时将一个文件(例如,一个日志文件或一个 tar-ed 备份)写入一个目录。我想要一种运行另一个 cron 作业的方法,该作业将删除该目录中最旧的文件,直到少于 5 个。
为了清楚起见,只有一个文件存在,它永远不应该被删除。
现有答案的问题:
rm
解决了这些问题,但解决方案是GNU特定的(并且非常复杂)。
这是一个实用的、符合 POSIX 的解决方案,它只有一个警告:它不能处理带有嵌入换行符的文件名——但我认为这对大多数人来说并不是现实世界的担忧。
作为记录,这里解释了为什么解析ls输出通常不是一个好主意:http: //mywiki.wooledge.org/ParsingLs
ls
ls -tp | grep -v '/$' | tail -n +6 | xargs -I {} rm -- {}
注意:该命令在当前*目录*下运行;要明确*定位目录,请使用带有以下命令的子shell( )(...)``cd*: (cd /path/to && ls -tp | grep -v '/$' | tail -n +6 | xargs -I {} rm -- {}) 这同样适用于以下命令**。
(...)``cd
(cd /path/to && ls -tp | grep -v '/$' | tail -n +6 | xargs -I {} rm -- {})
以上是低效的,因为必须为每个文件名单独xargs调用。 但是,您平台的具体实现可能允许您解决此问题:rm xargs
xargs
一个适用于*GNU* xargs的解决方案是使用-d '\n',这使得xargs将每个输入行视为一个单独的参数,但同时传递尽可能多的参数以适合命令行:
-d '\n'
ls -tp | grep -v '/$' | tail -n +6 | xargs -d '\n' -r rm --
注意: Option -r( --no-run-if-empty) 确保在没有 inputrm时不会调用它。
-r
--no-run-if-empty
一种适用于*GNU* *和* xargs *BSD* *(* xargs包括在macOS上)的解决方案——尽管在技术上仍然不符合 POSIX 标准——是在首先将换行符转换为( ) 字符之后使用 -separated 输入,这也传递(通常)所有文件-0名一次:NUL``NUL``0x0
-0
NUL``NUL``0x0
ls -tp | grep -v '/$' | tail -n +6 | tr '\n' '\0' | xargs -0 rm --
解释:
ls -tp
-t
/
-p
(cd /path/to && ls -tp ...)
grep -v '/$'
-v
/$
tail -n +6
N
N+1
tail -n +
xargs -I {} 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