8

这个问题类似于在 *nix 中清空目录最安全的方法是什么?

我正在编写 bash 脚本,它定义了几个路径常量,并将它们用于文件和目录操作(复制、重命名和删除)。通常有必要执行以下操作:

rm -rf "/${PATH1}"
rm -rf "${PATH2}/"*

在开发此脚本时,我想保护自己免受 PATH1 和 PATH2 之类的错误输入名称的影响,并避免将它们扩展为空字符串的情况,从而导致擦除整个磁盘。我决定创建特殊的包装器:

rmrf() {
    if [[ $1 =~ "regex" ]]; then
        echo "Ignoring possibly unsafe path ${1}"
        exit 1
    fi

    shopt -s dotglob
    rm -rf -- $1
    shopt -u dotglob
}

这将被称为:

rmrf "/${PATH1}"
rmrf "${PATH2}/"*

正则表达式(或 sed 表达式)应捕获诸如“*”、“/*”、“/**/”、“///*”等路径,但允许诸如“dir”、“/dir”、“/dir1”之类的路径/dir2/"、"/dir1/dir2/*"。此外,我不知道如何在“/dir with space/*”之类的情况下启用 shell globbing。有任何想法吗?

编辑:这是我到目前为止想出的:

rmrf() {
    local RES
    local RMPATH="${1}"
    SAFE=$(echo "${RMPATH}" | sed -r 's:^((\.?\*+/+)+.*|(/+\.?\*+)+.*|[\.\*/]+|.*/\.\*+)$::g')
    if [ -z "${SAFE}" ]; then
        echo "ERROR! Unsafe deletion of ${RMPATH}"
        return 1
    fi

    shopt -s dotglob
    if [ '*' == "${RMPATH: -1}" ]; then
        echo rm -rf -- "${RMPATH/%\*/}"*
        RES=$?
    else
        echo rm -rf -- "${RMPATH}"
        RES=$?
    fi
    shopt -u dotglob

    return $RES
}

预期用途是(注意引号内的星号):

rmrf "${SOMEPATH}"
rmrf "${SOMEPATH}/*"

其中 $SOMEPATH 不是系统或 /home 目录(在我的情况下,所有此类操作都是在 /scratch 目录下安装的文件系统上执行的)。

注意事项:

  • 没有很好地测试
  • 不打算与可能包含“..”或“.”的路径一起使用
  • 不应与用户提供的路径一起使用
  • 如果 $SOMEPATH 中的文件或目录过多(由于命令行长度有限),带有星号的 rm -rf 可能会失败 - 这可以通过“for”循环或“find”命令修复
4

10 回答 10

10

我发现 rm 在 bash 中的一个很大的危险是 bash 通常不会因为错误而停止。这意味着:

cd $SOMEPATH
rm -rf *

如果更改目录失败,这是一个非常危险的组合。更安全的方法是:

cd $SOMEPATH && rm -rf *

这将确保 rf 不会运行,除非你真的在 $SOMEPATH 中。这并不能保护您免受不良 $SOMEPATH 的影响,但可以将其与其他人提供的建议结合使用,以帮助您的脚本更安全。

编辑:@placeybordeaux 提出了一个很好的观点,即如果 $SOMEPATH 未定义或为空cd,则不会将其视为错误并返回 0。鉴于此,除非首先将 $SOMEPATH 验证为现有且非空,否则此答案应被视为不安全. 我相信cd没有 args 应该是一个非法命令,因为充其量是执行无操作,更糟糕的是它可能导致意外行为,但事实就是如此。

于 2009-06-14T13:45:53.150 回答
5

当使用未初始化的变量时,有一个set -ubash 指令会导致退出。我在这里读到了它,并举rm -rf了一个例子。我想这就是你要找的。这是set的手册

于 2009-07-23T02:37:48.197 回答
2

我认为“rm”命令有一个参数可以避免删除“/”。一探究竟。

于 2009-06-14T12:38:56.523 回答
2

我建议使用 realpath(1) 而不是直接使用命令参数,这样您就可以避免使用 /A/B/../ 或符号链接之类的东西。

于 2009-06-14T12:42:29.840 回答
2

一般来说,当我在开发一个带有' rm -fr'之类的操作的命令时,我会在开发过程中中和删除。一种方法是:

RMRF="echo rm -rf"
...
$RMRF "/${PATH1}"

这向我展示了应该删除的内容 - 但不会删除它。在开发过程中,我会进行手动清理——不冒把所有事情搞砸的风险,这是一个很小的代价。

符号“ "/${PATH1}"”有点不寻常;通常,您将确保 PATH1 仅包含绝对路径名。

使用带有 ' "${PATH2}/"*' 的元字符是不明智和不必要的。使用它和只使用 ' ' 之间的唯一区别"${PATH2}"是,如果 PATH2 指定的目录包含任何名称以点开头的文件或目录,那么这些文件或目录将不会被删除。这样的设计不太可能而且相当脆弱。仅仅通过 PATH2 并让递归删除完成它的工作会简单得多。添加尾部斜杠不一定是个坏主意;系统必须确保$PATH2包含一个目录名,而不仅仅是一个文件名,但额外​​的保护是相当少的。

使用带有 ' rm -fr' 的通配符通常是个坏主意。你想要精确、严格和限制它的作用——以防止事故。当然,在开发过程中,您永远不会以 root 身份运行命令(您正在开发的 shell 脚本)——那将是自杀。或者,如果绝对需要 root 权限,您可以取消删除操作,直到您确信它是防弹的。

于 2009-06-14T12:47:51.743 回答
2

您可以使用

set -f    # cf. help set 

禁用文件名生成 (*)。

于 2009-06-15T06:34:00.787 回答
1

同时我发现了这个 perl 项目:http ://code.google.com/p/safe-rm/

于 2009-06-14T14:10:14.463 回答
1

如果可能,您应该尝试将所有内容放入一个硬编码名称的文件夹中,该名称在文件系统的其他任何地方都不太可能找到,例如“ foofolder”。然后你可以把你的rmrf()函数写成:

rmrf() {
    rm -rf "foofolder/$PATH1"
    # or
    rm -rf "$PATH1/foofolder"
}

除了您想要的文件之外,该功能无法删除任何内容。

于 2009-06-15T01:55:47.250 回答
1

您不需要使用正则表达式。
只需将要保护的目录分配给一个变量,然后遍历该变量。例如:

protected_dirs="/ /bin /usr/bin /home $HOME"
对于 $protected_dirs 中的 d;做
    如果 [ "$1" = "$d" ]; 然后
        rm=0
        休息;
    菲
完毕
如果 [ ${rm:-1} -eq 1 ]; 然后
    rm -rf $1
菲
于 2009-10-28T19:56:02.413 回答
0

将以下代码添加到您的~/.bashrc

# safe delete
move_to_trash () { now="$(date +%Y%m%d_%H%M%S)"; mv "$@" ~/.local/share/Trash/files/"$@_$now"; }
alias del='move_to_trash'

# safe rm
alias rmi='rm -i'

每次你需要的rm东西,首先考虑del,你可以改变垃圾文件夹。如果您确实需要rm某些东西,您可以转到垃圾文件夹并使用rmi.

一个小错误del是,当del一个文件夹,例如,my_folder它应该del my_folder但不是del my_folder/因为为了以后可能恢复,我在最后附上了时间信息("$@_$now")。对于文件,它工作正常。

于 2018-03-11T04:36:25.347 回答