“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
这会移动dir
到d[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
。
最后:
该命令
md5sum 4800[48]3343/*
将返回错误“没有这样的文件或目录”,因为不存在与 glob 匹配的目录4800[48]3343
。该命令
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
如果您确实将目录重命名4800483343
为4800[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
将匹配480043343
or 480083343
。
其中都不是目录:4800[48]3343
,并且:不会进行扩展,ls 4800[48]3343/*
因为没有找到目录。更复杂的扩展将匹配目录,并且扩展将起作用:
$ ls 4800$'\133'[48][48]$'\135'3343/*
但可能在这里我对这个问题太深入了:-)