为什么星号在查找中会产生意外的结果?

为什么星号在查找中会产生意外的结果?

我是 Bash 新手。从查找文件文档

您想递归查找 ~/mydir 及其所有子目录中文件扩展名为 .htm(或 .HTM 或 .Htm...)的每个文件并将其删除。我见过很多尝试,例如 rm -rf ~/mydir/*.htm,但效果并不理想。正确的解决方案是

find ~/mydir -iname '*.htm' -exec rm {} \;

-iname表示您要对文件名进行不区分大小写的搜索。'*.htm'用单引号引起来是为了防止 bash 扩展 *,否则会产生意外结果。


问题:带引号和不带引号的星号会产生什么结果?为什么你决定使用引号而不是不使用引号?

答案1

如果不使用引号,shell 将扩展*.htm为文件列表并将此列表传递给find。如果使用引号,shell 将把五个字符的字符串*.htm作为参数提供给find

例如,引用:

$ ls
a.htm  b.htm
$ echo find . -iname '*.htm'
find . -iname *.htm
$ find . -iname '*.htm'
./b.htm
./a.htm

以上代码按预期运行。没有引号,find给出了它不知道如何处理的参数:

$ echo find . -iname *.htm
find . -iname a.htm b.htm
$ find . -iname *.htm
find: paths must precede expression: b.htm
Usage: find [-H] [-L] [-P] [-Olevel] [-D help|tree|search|stat|rates|opt|exec] [path...] [expression]

答案2

首先看一个例子,这里我们递归地查找所有带有.htm扩展名(不区分大小写)的文件:

$ tree 
.
├── spam
│   ├── egg.htm
│   └── egg.Htm
├── spam.htm
└── spam.Htm


$ find . -type f -iname *.htm
./spam.htm
./spam.Htm

$ find . -type f -iname '*.htm'
./spam.htm
./spam/egg.htm
./spam/egg.Htm
./spam.Htm

正如您所见,在第一种情况下,我没有使用任何引号-iname *.htm,文件名首先经过了 glob( *) 扩展(由 shell 在find启动之前完成),因为我没有使用任何引号,因此导致:

find . -type f -iname spam.htm spam.Htm

即当前目录中所有带有扩展名的文件.htm(不区分大小写)。因此,尽管./spam子目录中有两个带有.htm扩展名的文件,但它们不会被找到,因为我们现在正在寻找文件spam.htmspam.Htm

另一方面,在第二种情况下find . -type f -iname '*.htm',我们使用了引用,因此文件名不会受到全局扩展的影响,因此我们将得到所需的结果。

答案3

我相信你想了解的是一个称为“引用”的过程。引用适用于双引号和单引号。

如果将文本放在双引号内,则 shell 使用的所有特殊字符都将失去其含义,除了 $(美元符号)、\(反斜杠)和 `(反引号)。

单引号抑制所有扩展。

在这种情况下,转义字符也需要引起注意。您可以在字符前加上 \(反斜杠)以将其转换为转义字符。这通常在双引号内完成,以有选择地防止扩展。

echo "This bowl costs \$10.00"
This bowl costs $10.00

此处 $ 被称为转义字符。

相关内容