1
2
3
4
5
6
7
8
9
10
11
12
13
|
root@enabling-clam:~/testdir# echo "foolish rm" > "-rf" && ls -la
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# rm * && ls -la
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:~# mkdir testdir && cd testdir && ls -la
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# mkdir mydir1 && echo "Test1" > myfile1 && echo "Test2" > mydir1/myfile2 && ls -la
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# echo "foolish rm" > "-rf" && ls -la
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# rm * && ls -la
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# rm -rf && ls -la
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# rm "-rf" && ls -la
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# rm "./-rf" && ls -la
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 文件,有以下两种方法:
1
2
3
4
5
|
# Same as Example
rm ./-rf
# Another Example
rm -- -rf
|
上面两个例子都不需要给 -rf 加引号。
- 对于第一种写法,其提供给
rm 的参数首字符为点号 . ,以避免被 rm 当作短横线参数 - 处理。
- 第二种写法中,
-- 通常约定表示“选项到此结束”,位于 -- 后面的参数,即使以 - 开头,也应该被视为操作数而非选项。
Shell 层安全 != 命令语义安全