为什么方括号会阻止 shell 扩展?

为什么方括号会阻止 shell 扩展?

“4800483343”是一个目录,“file1”和“file2”是其中的两个文件。

为什么会出现以下情况?

$ ls 4800483343
file1 file2

$ md5sum 4800483343/*
36468e77d55ee160477dc9772a99be4b  4800483343/file1
29b098f7d374d080eb006140fb01bbfe  4800483343/file2

$ mv 4800483343 4800[48]3343

$ md5sum 4800[48]3343/*
md5sum: 4800[48]3343/*: No such file or directory

$ md5sum '4800[48]3343'/*
36468e77d55ee160477dc9772a99be4b  4800[48]3343/file1
29b098f7d374d080eb006140fb01bbfe  4800[48]3343/file2

还有哪些其他字符会导致此情况?

答案1

回答原来的问题

为什么方括号会阻止 shell 扩展

方括号不会阻止 shell 扩展,但引号会阻止。

我怀疑您实际运行的命令如下

这对以下文件运行 md5sum dir/

$ md5sum d[i]r/*
02fdd7309cef4d392383569bffabf24c  dir/file1
db69ce7c59b11f752c33d70813ab5df6  dir/file2

这会移动dird[i]r引号,以防止方括号的扩展:

$ mv dir 'd[i]r'

这会查找dir不再存在的目录:

$ md5sum d[i]r/*
d[i]r/*: No such file or directory

由于引号的存在,以下内容将在名为 的新目录中查找d[i]r

$ md5sum 'd[i]r'/*
02fdd7309cef4d392383569bffabf24c  d[i]r/file1
db69ce7c59b11f752c33d70813ab5df6  d[i]r/file2

回答修改后的问题

在修改后的问题中,目录4800483343存在并且运行以下命令:

mv 4800483343 4800[48]3343

运行此命令时会发生什么取决于 glob 是否与4800[48]3343任何现有目录匹配。如果没有目录匹配,则4800[48]3343扩展为自身4800[48]3343,并将目录4800483343移动到目录4800[48]3343

最后:

  1. 该命令md5sum 4800[48]3343/* 将返回错误“没有这样的文件或目录”,因为不存在与 glob 匹配的目录4800[48]3343

  2. 该命令md5sum '4800[48]3343'/*将正确找到文件,因为引号防止了全局扩展。

全局的示例

让我们创建两个文件:

$ touch a1b a2b

现在,观察这些球体:

$ echo a[123]b
a1b a2b
$ echo a?b
a1b a2b
$ echo *b
a1b a2b

答案2

括号表达式代表任意数量的单身的可能匹配的字符单身的字符位置。当您开始处理多字节字符并[[.整理元素.]]和/或[[=等价类时,这个简单的经验法则往往会变得模糊,=]]但无论如何,有许多程序还不完全支持这些字符的多字符匹配。对于任何区域设置,ASCII 数字 0-9 肯定会始终匹配自身并在括号表达式中按顺序进行整理。 POSIX 至少要求最后一位为真。

例如:

cd /tmp
for d in 0 1 2 3 4 5 6 7 8 9; do mkdir "$d"; done
ls [0123]

0:

1:  

2:

3:

基本上,单[括号表达式]shell glob 只是一个更具体的?any char glob。

echo ?/; echo [2468]/

0/ 1/ 2/ 3/ 4/ 5/ 6/ 7/ 8/ 9/
2/ 4/ 6/ 8/

因此 shell glob4800[48]3343将匹配以下两个当前目录文件名之一:

480043343
480083343

...但它不会匹配...

4800483343
4800843343
4800[48]3343

如果要在文件名中使用文字方括号,可以匹配括号表达式中的方括号:

touch 4800\[48]3343
echo 4800[[]48[]]3343

4800[48]3343

shell 的方括号通配符也不需要太多引用。 shell 将在列表上下文中将三个字符识别为 glob 表达式的开头:

* ? [

没有前导左方括号的右方括号只是另一个字符,不需要引用:

echo ]

]

因此,如果您只引用任何对的左方括号,它们将始终按字面解释。否则,您可以随时完全禁用通配符:

set -f

在您闲暇时将其重新打开:

set +f

答案3

如果您确实将目录重命名48004833434800[48]3343,那么您需要[ ]使用某种形式的引用来转义元字符。这一切都会起作用:

$ ls 4800\[48\]3343/*
$ ls "4800[48]3343"/*
$ ls '4800[48]3343'/*

全部都会正确打印:

4800[48]3343/file1  4800[48]3343/file2

但是,如果您保留元字符[并且]不加引号,它们将被解释为“路径名扩展”的括号描述。在 bash 手册中搜索“路径名扩展”,您会发现:

[...] 匹配任何一个包含的字符。

还有一些更多的文字解释了这是如何工作的。简而言之:

A [abc] will match ONE character, either an `a`, an `b` or an `c`.

因此,未加引号的4800[48]3343将匹配480043343or 480083343
其中都不是目录:4800[48]3343,并且:不会进行扩展,ls 4800[48]3343/*因为没有找到目录。更复杂的扩展将匹配目录,并且扩展将起作用:

$ ls 4800$'\133'[48][48]$'\135'3343/*

但可能在这里我对这个问题太深入了:-)

相关内容