这可能是一个荒谬的帖子,但假设我们有一个名为 的文件-|h4k3r|-
,为什么这个引用不起作用?
cat '-|h4k3r|-'
我认为单引号删除了所有特殊字符的含义?
然而这个有效
cat -- -\|h4k3r\|-
这也是:
cat "./-|h4k3r|-"
但我不明白为什么这不起作用?
cat "-|h4k3r|-"
我只是想理解为什么在这个例子中引用不能按预期工作。
'-
也许 Bash 对或"-
??有特殊含义
答案1
原因是,虽然单引号实际上删除了特殊字符的含义,但这指的是变量扩展、通配和分词- 即那些特殊的字符到外壳由 shell 解释在结果传递给程序之前- 和 shell 元字符(例如|
)。
从这个意义上来说,这-
不是一个“特殊字符”。它的特别之处在于它是一个事实上的向程序指示其开头的字符串是“选项”参数的标准方法。事实上,Unix/Linux 生态系统中的许多(如果不是大多数)程序都依赖于外部getopt()
用于此目的的函数1是 POSIX 规范的一部分,提供了处理命令行参数的标准化方法,并且解释以-
“选项”开头的参数的约定嵌入其中。
因此,单引号确保-|h4ker|-
从 shell 逐字传递到程序(cat
在您的情况下),但也删除了该过程中的引号。因此,程序仍然认为由于此参数以 a 开头-
,因此应将其视为“选项”参数,而不是操作数(如要处理的文件名)。
这就是为什么许多程序(同样,所有依赖于getopt()
)将--
命令行上的标记解释为特殊的“选项结束”指示符,以便它们可以安全地应用于以 开头的文件-
。
您在调查中已经探索过的另一种可能性是通过将文件名声明为绝对文件名 ( '/path/to/file/-|h4ker|-'
) 或添加当前目录 ( './-|h4ker|-'
) 来“保护”文件名,因为这样,参数将不再以“不明确”开头-
。请注意,在此示例中仍然需要引用/转义因为|
是一个 shell 元字符。
一个很好的演示是尝试创建并列出一个名为的文件-l
:
~$ touch '-l'
touch: Invalid option -- l
~$ touch -- '-l'
~$ ls '-l'
< ... the entire directory content in long list format ... >
~$ ls -- '-l'
-l
~$ ls -l -- '-l'
-rw-r--r-- 1 user user 0 Feb 24 17:16 -l
1对于 shell 脚本,有一个等效的getopts
内置命令
答案2
有二程序在这里工作。
一个是 shell,另一个是cat
程序/可执行文件/实用程序。
虽然 shell 将单引号保留为“引用”选项,但 cat 却没有。
shell 没有给初始破折号 ( -
) (在参数中)赋予特殊含义,但 cat 有。
shell 只是根据一些规则更改执行程序的参数:
$ echo '-|h4k3r|-'
-|h4k3r|-
$ echo "-|h4k3r|-"
-|h4k3r|-
$ echo -\|h4k3r\|-
-|h4k3r|-
该echo
命令接收相同的参数-|h4k3r|-
(删除引号后)并在上述所有三种情况下打印相同的结果。的引用|
删除了它的特别的对外壳的意义。
该echo
命令几乎没有选项,并且不能完全理解最初的破折号 ( -
),并且不需要执行任何其他操作。
但cat
确实有很多选项,如其man cat
页面中所述。选项通常以-
and开头全部后面的字母-
应由命令处理cat
(并非在所有操作系统中)。例如,cat -n file
会对文件中的每一行进行编号。
$ cat file
Line 1
Line 2
$ cat -n file
1 Line 1
2 Line 2
但如果我们options
以结尾--
并仍然添加 a -n
,它将被解释为文件名cat
:
$ cat -- -n file
cat: -n: No such file or directory
Line 1
Line 2
因为可以直接写入文件名(实际上是路径),以./
,../
或/
(本目录、上目录和根目录)开头。