1 2 3 4 5 6 7 8 9 10 11 12 13 root@enabling-clam:~/testdir total 8 -rw-r--r-- 1 root root 11 Jul 8 06:37 -rf drwxr-xr-x 1 root root 32 Jul 8 06:37 . drwx------ 1 root root 52 Jul 8 06:35 .. drwxr-xr-x 1 root root 14 Jul 8 06:36 mydir1 -rw-r--r-- 1 root root 6 Jul 8 06:36 myfile1 root@enabling-clam:~/testdir total 4 -rw-r--r-- 1 root root 11 Jul 8 06:37 -rf drwxr-xr-x 1 root root 6 Jul 8 06:37 . drwx------ 1 root root 52 Jul 8 06:35 ..
一段有趣的Bash命令记录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 root@enabling-clam:~ total 0 drwxr-xr-x 1 root root 0 Jul 8 06:35 . drwx------ 1 root root 52 Jul 8 06:35 .. root@enabling-clam:~/testdir total 4 drwxr-xr-x 1 root root 26 Jul 8 06:36 . drwx------ 1 root root 52 Jul 8 06:35 .. drwxr-xr-x 1 root root 14 Jul 8 06:36 mydir1 -rw-r--r-- 1 root root 6 Jul 8 06:36 myfile1 root@enabling-clam:~/testdir total 8 -rw-r--r-- 1 root root 11 Jul 8 06:37 -rf drwxr-xr-x 1 root root 32 Jul 8 06:37 . drwx------ 1 root root 52 Jul 8 06:35 .. drwxr-xr-x 1 root root 14 Jul 8 06:36 mydir1 -rw-r--r-- 1 root root 6 Jul 8 06:36 myfile1 root@enabling-clam:~/testdir total 4 -rw-r--r-- 1 root root 11 Jul 8 06:37 -rf drwxr-xr-x 1 root root 6 Jul 8 06:37 . drwx------ 1 root root 52 Jul 8 06:35 .. root@enabling-clam:~/testdir total 4 -rw-r--r-- 1 root root 11 Jul 8 06:37 -rf drwxr-xr-x 1 root root 6 Jul 8 06:37 . drwx------ 1 root root 52 Jul 8 06:35 .. root@enabling-clam:~/testdir total 4 -rw-r--r-- 1 root root 11 Jul 8 06:37 -rf drwxr-xr-x 1 root root 6 Jul 8 06:37 . drwx------ 1 root root 52 Jul 8 06:35 .. root@enabling-clam:~/testdir total 0 drwxr-xr-x 1 root root 0 Jul 8 06:38 . drwx------ 1 root root 52 Jul 8 06:35 .. root@enabling-clam:~/testdir
为什么?
这两个问题本质上都与 Bash 的 Shell展开
有关。
文件名展开
先看 rm * 一句命令,为什么这一行命令看起来像被解析成了
rm -rf mydir1 myfile1?
原因很简单:文件名通配符 * 遵照 Bash 的文件名展开 机制,在真正执行
rm 命令前就被处理完毕。
After word splitting, ... Bash scans each word for the characters
*, ?, and [ ... then the word is
regarded as a pattern, and replaced with a sorted list of filenames
matching the pattern
也就是说,rm 命令接收到的,是经过 Bash
展开后的文件名列表,在上面的例子中就等价于:
双引号处理
我们还注意到,rm "-rf" 一句命令并没能成功移除
-rf 文件。明明我们已经试着用引号告诉 rm
这是一个整体,为什么还会失败呢?
在 Bash
中,经过参数展开、文件名展开等处理的命令在传递给程序前,还会最终经过 引号移除 。
引号移除发生在所有展开操作的最后,所有未经引号或转移保护的斜杠、单双引号都会被移除。
这也就是说,对于上面的例子,双引号对 -rf
的保护仍然局限在 Shell
处理阶段,保护其中字符的字面值,阻止空格分词。
然而,稍后 rm 命令收到的仍然是 -rf
字符串,并在稍后将其解析为 -r 和 -f 两个
option,最后还是没能将其作为要删除的文件名称进行处理。
解决方案与启示
想要正确删除 -rf 文件,有以下两种方法:
上面两个例子都不需要给 -rf 加引号。
对于第一种写法,其提供给 rm 的参数首字符为点号
. ,以避免被 rm 当作短横线参数 -
处理。
第二种写法中,-- 通常约定表示“选项到此结束”,位于
-- 后面的参数,即使以 -
开头,也应该被视为操作数而非选项。
Shell 层安全 != 命令语义安全