所以,我删除了我的主文件夹(或者更准确地说,我有写权限的所有文件)。发生的事情是我有
build="build"
...
rm -rf "${build}/"*
...
<do other things with $build>
在 bash 脚本中,并且在不再需要之后$build
,删除声明及其所有用法——但是rm
. Bash 愉快地扩展为rm -rf /*
. 是的。
我觉得自己很愚蠢,安装了备份,重新完成了我丢失的工作。试图摆脱耻辱。
现在,我想知道:编写 bash 脚本的技术是什么,这样就不会发生这种错误,或者至少不太可能发生这种错误?例如,我是否写过
FileUtils.rm_rf("#{build}/*")
在 Ruby 脚本中,解释器会抱怨build
没有被声明,所以语言保护了我。
我在 bash 中考虑的内容,除了 corraling rm
(正如相关问题中的许多答案所提到的,这并非没有问题):
rm -rf "./${build}/"*
那会杀死我当前的工作(一个 Git 存储库),但没有别的。rm
在当前目录之外操作时,它的变体/参数化需要交互。(找不到任何东西。)类似的效果。
是这样,还是有其他方法可以编写在这个意义上“健壮”的 bash 脚本?
或者
这将使当前的 shell 将未设置变量的扩展视为错误:
set -u
并且set -o nounset
是POSIX shell 选项。但是,空值不会触发错误。
为此,使用
的扩展
${variable:?word}
将扩展为的值,variable
除非它为空或未设置。如果它为空或未设置,word
则会在标准错误中显示,并且 shell 会将扩展视为错误(不会执行命令,如果在非交互式 shell 中运行,则会终止)。离开:
会仅针对未设置的值触发错误,就像 under 一样set -u
。${variable:?word}
是一个POSIX 参数扩展。set -e
除非(或set -o errexit
)也生效,否则这些都不会导致交互式 shell 终止。${variable:?word}
如果变量为空或未设置,则导致脚本退出。set -u
如果与 . 一起使用会导致脚本退出set -e
。至于你的第二个问题。没有办法限制
rm
不能在当前目录之外工作。的 GNU 实现
rm
有一个--one-file-system
选项可以阻止它递归地删除已挂载的文件系统,但是我相信我们可以在不将rm
调用包装在一个实际检查参数的函数中的情况下得到尽可能接近的结果。作为旁注:
${build}
完全等同于$build
除非扩展作为字符串的一部分出现,其中紧随其后的字符是变量名中的有效字符,例如 in"${build}x"
。我将建议使用
test
/[ ]
如果您这样编写脚本,您将是安全的:
[ -n "${build}" ]
检查是否"${build}"
为非零长度字符串。||
是 bash 中的逻辑 OR 运算符。如果第一个命令失败,它将导致另一个命令运行。这样,就
${build}
一直是空的/未定义的/等。该脚本将退出(返回代码为 1,这是一个一般错误)。如果您删除所有用途,这也可以保护您,
${build}
因为这[ -n "" ]
将永远是错误的。使用
test
/的优点[ ]
是它还可以使用许多其他更有意义的检查。例如:
在您的特定情况下,我过去重新设计了“删除”以移动文件/目录(假设 /tmp 与您的目录位于同一分区):
在幕后,这会将顶级文件/目录引用从源目录结构移动
$trashdir
到同一分区上的目标目录结构,并且不会花时间遍历目录结构并立即释放每个文件的磁盘块。这会在系统处于活动使用状态时产生更快的清理速度,以换取稍慢的重新启动(/tmp 在重新启动时被清理)。或者,定期清理 /tmp/.trash-$USER 的 cron 条目将防止 /tmp 填满,用于消耗大量磁盘空间的进程(例如,构建)。如果您的目录与 /tmp 位于不同的分区上,您可以在您的分区上创建一个类似 /tmp 的目录,然后让 cron 清理它。
但是,最重要的是,如果您以任何方式搞砸了变量,您可以在清理发生之前恢复内容。
当变量未初始化时,使用 bash 参数替换来分配默认值,例如:
rm -rf ${变量:-“/不存在”}
我总是尝试用一行开始我的 Bash 脚本
#!/bin/bash -ue
。-e
意思是“在第一个未解决的错误上失败”;-u
意思是“第一次使用未声明的变量时失败”。在伟大的文章Use the Unofficial Bash Strict Mode (Unless You Looove Debugging)中找到更多详细信息。作者还建议使用
set -o pipefail; IFS=$'\n\t'
,但出于我的目的,这是矫枉过正的。检查您的变量是否已设置的一般建议是防止此类问题的有用工具。但在这种情况下,有一个更简单的解决方案。
很可能不需要 glob
$build
目录的内容来删除它们,而不是实际的$build
目录本身。因此,如果您跳过了无关项*
,那么未设置的值将变成rm -rf /
默认情况下过去十年中大多数 rm 实现将拒绝执行的值(除非您使用 GNU rm 的--no-preserve-root
选项禁用此保护)。跳过尾随
/
也会rm ''
导致错误消息:即使您的 rm 命令没有对
/
.您正在考虑编程语言术语,但bash是一种脚本语言 :-) 因此,对于完全不同的语言范例,请使用完全不同的指令范例。
在这种情况下:
由于
rmdir
将拒绝删除非空目录,因此您需要先删除成员。你知道那些成员是什么,对吧?它们可能是脚本的参数,或者是从参数派生的,所以:现在,如果您将一些其他文件或目录放入其中,例如临时文件或您不应该放入的其他文件,
rmdir
则会引发错误。妥善处理,然后:这种思维方式对我很有帮助,因为……是的,去过那里,做到了。