将来更改时,如何强化 bash 脚本以防止造成伤害?

Rap*_*ael 47 bash shell-script rm

所以,我删除了我的主文件夹(或者,更准确地说,我有写权限的所有文件)。发生的事情是我有

build="build"
...
rm -rf "${build}/"*
...
<do other things with $build>
Run Code Online (Sandbox Code Playgroud)

在 bash 脚本中,在不再需要之后$build,删除声明及其所有用法——但是rm. Bash 愉快地扩展到rm -rf /*. 是的。

我觉得自己很傻,安装了备份,重新做我丢失的工作。试图摆脱耻辱。

现在,我想知道:编写 bash 脚本的技巧是什么,以便此类错误不会发生,或者至少不太可能发生?例如,我是否写过

FileUtils.rm_rf("#{build}/*")
Run Code Online (Sandbox Code Playgroud)

在 Ruby 脚本中,解释器会抱怨build没有被声明,所以语言保护了我。

我在 bash 中考虑过的,除了 corraling rm(正如相关问题中提到的许多答案一样,这并非没有问题):

  1. rm -rf "./${build}/"*
    那会扼杀我目前的工作(Git repo),但没有别的。
  2. rm在当前目录之外操作时,它的变体/参数化需要交互。(找不到。)类似的效果。

是这样,还是有其他方法可以编写在这个意义上“健壮”的 bash 脚本?

Kus*_*nda 77

set -u
Run Code Online (Sandbox Code Playgroud)

或者

set -o nounset
Run Code Online (Sandbox Code Playgroud)

这将使当前的 shell 将未设置变量的扩展视为错误:

$ unset build
$ set -u
$ rm -rf "$build"/*
bash: build: unbound variable
Run Code Online (Sandbox Code Playgroud)

set -u并且set -o nounsetPOSIX shell 选项

但是,不会触发错误。

为此,使用

$ rm -rf "${build:?Error, variable is empty or unset}"/*
bash: build: Error, variable is empty or unset
Run Code Online (Sandbox Code Playgroud)

的扩展${variable:?word}将扩展到值,variable除非它为空或未设置。如果它为空或未设置,word则将显示在标准错误上,并且 shell 会将扩展视为错误(该命令不会被执行,如果在非交互式 shell 中运行,这将终止)。离开:了会引发错误只为一个没有设置的,就像下set -u

${variable:?word}POSIX 参数扩展

这些都不会导致交互式 shell 终止,除非set -e(或set -o errexit) 也有效。${variable:?word}如果变量为空或未设置,则导致脚本退出。set -u如果与set -e.


至于你的第二个问题。没有办法限制rm在当前目录之外不能工作。

的 GNU 实现rm有一个--one-file-system选项,可以阻止它递归删除挂载的文件系统,但这与我相信我们可以在不将rm调用包装在实际检查参数的函数中一样接近。


作为旁注: ${build}完全等同于$build除非扩展作为字符串的一部分发生,其中紧随其后的字符是变量名称中的有效字符,例如 in "${build}x"

  • @Raphael 好的,我添加了一个简短的句子,说明没有 `:` 会发生什么(它只会触发 _unset_ 变量的错误)。我不敢尝试编写一个工具,在任何情况下都可以专门处理特定路径下的文件删除。目前,解析路径和关心/不关心符号链接等对我来说有点过于繁琐。 (2认同)
  • @MateuszKonieczny 我不认为我会,对不起。我认为应该在_需要时_使用此类安全措施。与使环境安全的一切事物一样,它最终会使人们越来越粗心并依赖于安全措施。最好了解每项安全措施的作用,然后根据需要有选择地应用它们。 (2认同)

Cen*_*ane 12

我将建议使用test/[ ]

如果你这样写你的脚本,你会很安全:

build="build"
...
[ -n "${build}" ] || exit 1
rm -rf "${build}/"*
...
Run Code Online (Sandbox Code Playgroud)

[ -n "${build}" ]检查到"${build}"是一个非零的长度的字符串

||是在bash逻辑OR运算符。如果第一个命令失败,它会导致运行另一个命令。

就这样,一直${build}是空的/未定义的/等。脚本会退出(返回码为 1,这是一个通用错误)。

如果您删除所有用途,这也会保护您,${build}因为[ -n "" ]它将始终为假。


使用test/的好处[ ]是还有许多其他更有意义的检查可以使用。

例如:

[ -f FILE ] True if FILE exists and is a regular file.
[ -d FILE ] True if FILE exists and is a directory.
[ -O FILE ] True if FILE exists and is owned by the effective user ID.
Run Code Online (Sandbox Code Playgroud)