预防

预防

我使用 Bash 4.3.48(1),并在测试 VPS 机器上运行以下命令模式:

rm -rf ${drt}/${pma}*

该命令删除了整个操作系统(Ubuntu)。通过执行可以明显看出这一点,cd /除了以下错误之外什么也没返回:

bash: cd /: 没有这样的文件或目录

更深入地观察,发生这种情况是因为上面原始命令中的两个变量都没有声明:

  1. 我创建了一个名为 的文件~/repoName/assignments_variables.sh,其中包含导出变量的列表(包括drtpma)。

  2. 相反source /etc/bash.bashrc,我运行了running /etc/bash.bashrc,这是错误的,因为该文件仅与当前会话相关(尽管它可用于随后运行自己在子会话中执行数据的文件)。


现在我很清楚错误的路径是问题的一般原因,我想更深入地探讨一下并问这个问题:

为什么 会rm -rf忽略错误的变量扩展并继续转到/*?我知道rm -rf应该删除一个目录仅有的如果找到它,则不应依赖于不存在的目录的部分路径(就像/*导致删除操作系统一样)。我如何改进该rm -rf命令以涵盖将来可能出现的类似错误路径情况(由于错误的变量扩展)?

是否有一些 Bash 指令可以确保 Bash 永远不会扩展空变量(直到我撤消该指令)?


让我强调一下:我通常不仅进行备份,而且进行双重备份。这确实是一个后台有 x3 备份的测试环境。

答案1

如果变量不存在,会扩展什么?
让我们尝试一下(注意回声):

$ unset drt pma
$ echo rm -rf ${drt}/${pma}*
rm -rf /bin /boot /dev /etc /hello /home /initrd.img /lib /lib32 /lib64 /libx32 /media /mnt /opt /proc /root /run /sbin /srv /sys /tmp /usr /var /vmlinuz /vmlinuz.old

这就是整个系统(正如您已经确认的那样)。
问题是由 shell 完成的扩展。

预防

我们如何防止这种情况发生?
通过防止 shell 扩展空变量。如果 var 为空或未设置,则产生
扩展。${var:?message}message
停止执行(严重错误)。测试一下:

$ echo rm -rf "${drt:?Missing variable drt}"/"${pma:?Missing variable pma}"*

而且这个解决方案是 POSIX 兼容的。

答案2

正如已经指出的,这两个变量drtpma并不存在于您的脚本环境中,这意味着 shell 将它们扩展为空字符串。生成的命令是rm -rf /**进一步扩展到根目录中可用的任何命令)。

以 root 身份运行不会删除根目录本身rm -rf /*。另外,我(和杰西_b谁向我指出的)我对错误消息有点困惑

bash: cd /: No such file or directory

我自己能够引发此错误消息的唯一方法是运行命令"cd /"(注意引号)。如果/确实被删除,错误消息会说

bash: cd: /: No such file or directory

isaac已经给出了一些提示如何防止你将来再做这样的事情,但我想我只是提出我的建议。

  • 不要在 root shell 提示符下工作。
  • 如果编写需要root权限的脚本,请sudo在脚本中使用具体的实际需要这些提升权限的命令。不要以 root 身份运行整个脚本,尤其不在开发过程中。
  • 您通常不需要/etc/bash.bashrc出于任何原因进行接触,并且您永远不必明确地提供它。环境变量可以创建在
    • 用户自己的$HOME/.bashrc(如果要从交互式会话运行脚本),或者
    • 在脚本本身中,或者
    • 在一个单独的文件中(写为为了脚本)由脚本明确来源,或者
    • 在环境变量指向的文件中BASH_ENV

在这种情况下,另一个可以拯救您的安全措施是在set -u( nounset) 下运行脚本,也可能在set -e( errexit) 下运行您的脚本。 shellnounset选项会将未设置变量的扩展视为错误,而errexit如果命令以非零退出状态退出,则 shell 选项将立即退出 shell 会话。

剧本

#!/bin/bash -ue

echo "$hello"
echo "world"

永远不会输出world,而是会引发消息

script.sh: line 3: hello: unbound variable

在终止之前。 (它-u执行终止操作,-e但不执行任何操作在这个简单的例子中)。

答案3

变量没有设置插值,所以运行的是 glob rm -rf /*,它下面的所有东西都是/坏的,正如你发现的那样。

添加错误检查。并且,还要引用 ,"$variables"因为否则 shell 会对它们进行 split-glob ,然后当$variables包含空格或其他有问题的字符时,可能会发生愚蠢的事情。特别是当rm -rf涉及到a时。

if [[ -z "$drt" ]]; then
    echo >&2 "drt variable not set ??"
    exit 1
fi
if [[ -z "$pma" ]]; then
    echo >&2 "pma variable not set ??"
    exit 1
fi
rm -rf "${drt}"/"${pma}"*

(或者使用问题较少的语言进行编程......)

相关内容