鉴于这六个文件:
$ touch 'sec*et' 'sec\*et' 'sec\et' secet secret 'sec\ xxx et'
为什么用于全局扩展的未加引号变量中的反斜杠仅匹配sec\*et
文件?
$ v="sec\*et" ; ls $v
'sec\*et'
$ v='sec\*et' ; ls $v
'sec\*et'
< 反斜杠 > 应保留其作为转义字符的特殊含义...仅当后跟以下字符之一时被认为是特殊的:
$
`
"
\
<newline>
和bash手册:
仅当反斜杠后跟以下字符之一时,反斜杠才保留其特殊含义:'
$
'、'`
'、'"
'、'\
' 或换行符。在双引号内,后跟这些字符之一的反斜杠将被删除。前面没有特殊含义的反斜杠字符保持不变。
我知道变量中的反斜杠(星号之前)是字面反斜杠:
$ v='sec\*et' ; printf '%s' "$v" | hexdump -C
00000000 73 65 63 5c 2a 65 74 |sec\*et|
00000007
但我不明白为什么通配符*
在字面反斜杠之后失去了特殊含义。
我明白的三件事:
*
(A)未加引号的变量中的星号在全局扩展中具有特殊含义。它们是等价的:
$ v='sec*et' ; ls $v
'sec*et' 'sec\*et' 'sec\et' secet secret 'sec\ xxx et'
$ ls sec*et
'sec*et' 'sec\*et' 'sec\et' secet secret 'sec\ xxx et'
(B) 星号*
在反斜杠之后失去其特殊含义:
$ ls sec\*et
'sec*et'
(C) 字面上的反斜杠不能使星号*
失去其特殊含义:
$ v='sec\\*et' ; ls $v
'sec\*et' 'sec\et' 'sec\ xxx et'
$ ls sec\\*et
'sec\*et' 'sec\et' 'sec\ xxx et'
这是我不明白的:
然而奇怪的是,星号失去其特殊含义,但反斜杠并未被丢弃, 在这种情况下:
$ v='sec\*et' ; ls $v
'sec\*et'
不知何故,它相当于一个字面反斜杠后跟一个字面星号:
$ ls sec\\\*et
'sec\*et'
但为什么?考虑:
如果引号中的特殊字符变成文字,则 (A) 不成立。
如果星号因为后面有反斜杠而变成文字,为什么反斜杠不会被丢弃,并匹配 file
sec*et
,就像 (B) 中那样?
在应用中,除了使用 Bracket Expression 之外[*]
,如何定义在 glob 扩展中使用时与文字星号匹配的字符串变量?
$ v='sec < what what what > et' ; ls $v
'sec*et'
答案1
$ v="sec\*et"
$ v='sec\*et'
其中任何一个都会将变量设置为sec\*et
,并且变量获取其值的方式不会影响通配符的行为。
Bash 处理后续未加引号的扩展的方式似乎是,由于它没有未转义的 glob 字符,因此该字符串sec\*et
不会被视为 glob根本不。它不会触发 eg failglob
,并且反斜杠不会被删除。相反,它只是按原样打印。 (这不是模式匹配本身:即使没有该名称的文件,您也会返回字符串。)
sec[*]et
这与是一团。
另外,如果您要使用sec\*et*
它,它将是一个 glob,并且会匹配以 开头的文件名sec*et
(即删除反斜杠)。
据我所知,大多数版本的 Bash 都是这样工作的。 Bash 5.0 是个例外,它sec\*et
被视为一个 glob,并且会触发例如failglob。
$ ./bash-4.4/bash -c 'shopt -s failglob; v="sec\*et"; echo $v'
sec\*et
$ ./bash-5.0/bash -c 'shopt -s failglob; v="sec\*et"; echo $v'
./bash-5.0/bash: no match: sec\*et
$ ./bash-5.1.16/bash -c 'shopt -s failglob; v="sec\*et"; echo $v'
sec\*et
$ ./bash-5.2.15/bash -c 'shopt -s failglob; v="sec\*et"; echo $v'
sec\*et
(我没有测试所有情况下的所有 shell 版本。)