如何获取目录中的第一个子文件夹并将其删除?
我找到了一个监控可用磁盘空间的脚本。
如果空间不足,它会发送警报电子邮件,但我也想删除一些不需要的东西。
我有一个备份文件夹,用于保存每日和每月的备份。我想删除第一个文件夹,因为它总是最旧的,但我不知道最旧的备份的名称。
我的文件夹没有 Jan-May 和 Dec:
06-01
07-01
08-01
09-01
10-01
11-01
Friday
Monday
Saturday
Sunday
Thursday
Tuesday
Wednesday
如何在不知道其名称的情况下删除第一个文件夹“06-01”?
答案1
如果你想找到第一个子文件夹关于字母数字排序(请注意,取决于您的本地设置),您可以使用这个小 Bash 代码片段:
shopt -s nullglob
shopt -u dotglob
a=( */ )
echo "${a[0]}"
这只会呼应第一个子文件夹关于 Bash 的字母数字排序。
如果要删除它,请添加一些保护:
shopt -s nullglob
shopt -u dotglob
a=( */ )
[[ -d "${a[0]}" ]] && echo rm -rfv -- "${a[0]}"
现在,我不确定我是否会在生产服务器上使用它。这取决于当然我是目录的内容是(和我有多确定备份)。
和shopt -s nullglob
是shopt -u dotglob
为了避免如果设置不正确而可能出现的愚蠢错误。另外,请注意引号,不要对其进行任何更改。
哦,我echo
在 前面留了一个rm
。只有经过多次测试,确定它完全按照预期工作时,才可以将其删除。一行代码:
shopt -s nullglob; shopt -u dotglob; a=( */ ); [[ -d "${a[0]}" ]] && echo rm -rfv -- "${a[0]}"
如果要指定子文件夹所在的路径,只需将语句替换a=( */ )
为:
a=( /path/to/folder/*/ )
确保输入的是绝对路径,并以*/
(带斜杠)结尾。如果路径中有空格,请使用引号,如下所示:
a=( '/path with/spaces to/folder/'*/ )
(观察 如何*/
在引用部分之外,并且在结束引号和 之后没有空格*/
)。
现在如果你真的想要确定最旧的子文件夹(如果多个子文件夹的年龄相同,则具有一定的不确定性),您可以(颤抖)按时间相反的顺序解析排序的输出ls
,然后取出第一个并将其删除,如下所示:
ls -1tr --group-directories-first /path/to/folder | head -1 | xargs echo rm -rfv
但如果没有目录,此操作将会失败(您将删除最旧的文件否则,如果文件包含空格或奇怪的符号,则会失败。不要这样做。
您可以使用以下选项来要求ls */
不列出目录内容-d
:
ls -1dtr path/to/folder/*/ | head -1 | xargs echo rm -rfv
但如果名称中有空格,则此操作将会失败。不要这样做。
然后,您可以尝试使用该-Q
选项ls
(即引用输出)来修复此问题:
ls -1dtQr path/to/folder/*/ | head -1 | xargs echo rm -rfv
但如果文件名中有引号或换行符,则会失败。不要这样做。
然后您可以尝试xargs
以下-0
选项:
ls -1dtQr path/to/folder/*/ | head -1 | xargs -0 echo rm -rfv
但如果文件名中有换行符,则会失败。不要这样做。
那么你可以... 停止!
如果你真的想要一种 100% 可靠的方法,那么这可能不是最有效的,但如果安全是你的首要任务,那么这种方法就适合你,这里有一种可能性(在 Bash 中):
shopt -s nullglob
shopt -u dotglob
where="/path with spaces is ok/to/folder"
for i in "$where"/*/; do
if ! find "$where" -mindepth 1 -maxdepth 1 \! -name '.*' -type d \! -newer "$i" \! -samefile "$i" -print | read; then
echo rm -rfv -- "$i"
break
fi
done
这不是最有效的方法,但效果很好:它循环遍历i
给定目录中的所有(第一级)目录where
,并启动find
以获取比目录更旧的(非隐藏)目录i
,如果找到任何目录,则不执行任何操作;如果没有找到,则我们中断echo rm
它并中断循环。副作用是,将要删除的目录是 Bash 的字母数字排序中最旧且最先的目录。
你甚至可以用它制作一个很酷的脚本(例如,rm_oldest_dir
):
#!/bin/bash
if [[ $1 = --serious ]]; then
serious=()
shift
else
serious=("echo")
fi
if [[ -z $1 ]]; then
echo >&2 "You must provide an argument"
exit 1
fi
where=$1
if [[ ! -d $where ]]; then
echo >&2 "\`$where' is not a directory"
exit 1
fi
shopt -s nullglob
shopt -u dotglob
for i in "$where"/*/; do
if ! find "$where" -mindepth 1 -maxdepth 1 \! -name '.*' -type d \! -newer "$i" \! -samefile "$i" -print | read; then
"${serious[@]}" rm -rfv -- "$i"
if ((${#serious[@]})); then
echo "*** Nothing has been removed. Call with option --serious to really remove. ***"
fi
break
fi
done
然后使用,例如,
$ mkdir Test
$ cd Test
$ mkdir oldest; sleep 1; mkdir lots_of_dirs{1..10}
$ rm_oldest_dir .
rm -rfv -- ./oldest/
*** Nothing has been removed. Call with option --serious to really remove. ***
$ ls
lots_of_dirs1 lots_of_dirs3 lots_of_dirs6 lots_of_dirs9
lots_of_dirs10 lots_of_dirs4 lots_of_dirs7 oldest
lots_of_dirs2 lots_of_dirs5 lots_of_dirs8
$ rm_oldest_dir --serious .
removed directory: `./oldest'
$ ls
lots_of_dirs1 lots_of_dirs2 lots_of_dirs4 lots_of_dirs6 lots_of_dirs8
lots_of_dirs10 lots_of_dirs3 lots_of_dirs5 lots_of_dirs7 lots_of_dirs9
$
整洁的。
然后你可以把它放在你的 crontab 中
0 0 * * * rm_oldest_dir --serious "/path with spaces/to/where/you/want"
我能想到的唯一针对此脚本的攻击是文件夹太多:Bash 的通配符效率不高,文件夹太多可能会导致 CPU 使用率长时间过高,最终以失败告终。更复杂的方法是基于文件的半快速排序,但由于这篇文章已经太长了,我将在这里结束。
干杯!
我不知道这个答案的第二部分是否真的适合 SF。如果没有,请在评论中告诉我或点赞此帖子!