此问题与What is the safest way to empty a directory in *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不是system或/ home目录(在我的例子中,所有这些操作都是在/ scratch目录下挂载的文件系统上执行的。)
注意事项:
答案 0 :(得分:7)
我发现bash中的rm存在很大危险,因为bash通常不会因错误而停止。这意味着:
cd $SOMEPATH
rm -rf *
如果更改目录失败,则是非常危险的组合。更安全的方式是:
cd $SOMEPATH && rm -rf *
除非你真的在$ SOMEPATH中,否则这将确保rf不会运行。这并不能保护您免受错误的$ SOMEPATH的影响,但它可以与其他人提供的建议相结合,以帮助您使脚本更安全。
编辑:@placeybordeaux指出,如果$ SOMEPATH未定义或为空cd
不将其视为错误并返回0.鉴于此答案应视为不安全,除非$ SOMEPATH是首先验证为现有和非空。我认为没有args的cd
应该是一个非法的命令,因为最好是执行无操作,更糟糕的是它可能导致意外的行为,但它就是它。
答案 1 :(得分:4)
当使用未初始化的变量时,有一个set -u
bash指令将导致退出。我阅读了here,以rm -rf
为例。我认为这就是你要找的东西。这是set's manual。
答案 2 :(得分:2)
我认为“rm”命令有一个参数可以避免删除“/”。看看吧。
答案 3 :(得分:2)
我建议直接使用realpath(1)而不是命令参数,这样就可以避免使用/A/B/../或符号链接。
答案 4 :(得分:2)
通常,当我在开发一个包含“rm -fr
”等操作的命令时,我会在开发期间中和删除。一种方法是:
RMRF="echo rm -rf"
...
$RMRF "/${PATH1}"
这显示我应删除的内容 - 但不删除它。当事情正在开发中时,我会做一个手动清理工作 - 这是一个很小的代价,因为它不会冒险搞砸一切。
符号“"/${PATH1}"
”有点不寻常;通常,您将确保PATH1只包含绝对路径名。
将元字符与“"${PATH2}/"*
”一起使用是不明智和不必要的。使用它和仅使用“"${PATH2}"
”之间的唯一区别是,如果PATH2指定的目录包含名称以dot开头的任何文件或目录,则不会删除这些文件或目录。这种设计不太可能,而且相当脆弱。传递PATH2并让递归删除完成它的工作会简单得多。添加尾部斜杠不一定是个坏主意;系统必须确保$PATH2
包含目录名,而不仅仅是文件名,但额外的保护是相当小的。
对'rm -fr
'使用globbing通常是一个坏主意。你想要精确和限制,并限制它的作用 - 以防止事故。当然,你永远不会在开发过程中以root身份运行命令(你正在开发的shell脚本) - 这将是自杀性的。或者,如果绝对需要root权限,则可以中和删除操作,直到您确信它是防弹为止。
答案 5 :(得分:1)
与此同时,我发现了这个perl项目:http://code.google.com/p/safe-rm/
答案 6 :(得分:1)
如果可能,您应该尝试将所有内容放入具有硬编码名称的文件夹中,该文件夹不太可能在文件系统的任何其他位置找到,例如“foofolder
”。然后,您可以将rmrf()
函数编写为:
rmrf() {
rm -rf "foofolder/$PATH1"
# or
rm -rf "$PATH1/foofolder"
}
除了你想要的文件外,该函数无法删除任何内容。
答案 7 :(得分:1)
您可以使用
set -f # cf. help set
禁用文件名生成(*)。
答案 8 :(得分:1)
您不需要使用正则表达式 只需将要保护的目录分配给变量,然后迭代变量即可。例如:
protected_dirs="/ /bin /usr/bin /home $HOME" for d in $protected_dirs; do if [ "$1" = "$d" ]; then rm=0 break; fi done if [ ${rm:-1} -eq 1 ]; then rm -rf $1 fi
答案 9 :(得分: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"
)。对于文件,它工作正常。