Ubuntu 服务器:删除目录中的第一个文件夹

Ubuntu 服务器:删除目录中的第一个文件夹

如何获取目录中的第一个子文件夹并将其删除?

我找到了一个监控可用磁盘空间的脚本。

如果空间不足,它会发送警报电子邮件,但我也想删除一些不需要的东西。

我有一个备份文件夹,用于保存每日和每月的备份。我想删除第一个文件夹,因为它总是最旧的,但我不知道最旧的备份的名称。

我的文件夹没有 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 nullglobshopt -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。如果没有,请在评论中告诉我或点赞此帖子!

相关内容