文件名扩展:查找实用程序模式匹配与 Bash Shell 模式匹配

tea*_*144 6 shell bash find wildcards

对于文件名扩展,'find' 实用程序的 '-name' 选项似乎功能类似,但与 bash shell 的内置模式匹配并不完全相同。

以下是 GNU 参考手册的相关部分:

这本身就非常令人困惑。为了增加这种混乱,'find' 实用程序的手册页(上面引用)的第 2.1.4 节标题为“Shell 模式匹配”,这意味着 'find' 正在使用 shell 的内置模式匹配功能。但是,情况似乎并非如此,因为根据“查找”手册页(http://goo.gl/ngQTKx),在“-name 模式”下,它表示以下内容:

“文件名匹配是使用 fnmatch(3) 库函数执行的。不要忘记用引号将模式括起来,以防止它被 shell 扩展。”

由此看来,执行模式匹配的不是 shell,而是使用 fnmatch 库的 find 实用程序。

以下是我的问题:

  1. bash shell 的默认文件名扩展和模式匹配(禁用 extglob shell 选项)是否与使用 -name 选项的 find 实用程序不同?
  2. 如果是这样,这些区别是什么?
  3. bash 是否还使用 fnmatch 库或其他一些机制来进行文件名扩展和模式匹配?

Sté*_*las 6

在 shell 中,您需要将文件名生成/扩展(又名globbing:扩展为文件列表的模式)与模式匹配区分开来。globbing 在内部使用模式匹配,但它确实在所有操作员之前基于模式生成文件列表。

*/*.txt是一种模式,它匹配 0 个或多个字符的序列,然后是/,然后是零个或多个字符的序列,然后是.txt。当用作外壳模式时,如下所示:

case $file in
  */*.txt) echo match
esac
Run Code Online (Sandbox Code Playgroud)

它将匹配file=.foo/bar/baz.txt.

但是,*/*.txt作为glob是相关但更复杂的东西。

在扩展*/*.txt为文件列表时,shell 将打开当前目录,列出其内容,找到匹配的目录类型(或目录的符号链接)的非隐藏文件*,对该列表进行排序,打开每个目录,列出其内容,并找到匹配的非隐藏的*.txt

.foo/bar/bar.txt即使它与模式匹配,它也永远不会扩展,因为它不是这样工作的。另一方面,由glob生成的文件路径都将匹配该模式。

同样,像水珠foo[a/b]baz*会发现他的名字开始的所有文件b]bazfoo[a目录。

所以,我们已经看到,对于 globbing 而不是模式匹配,它/是特殊的(globs 以某种方式被拆分/,每个部分单独处理)并且 dot-files 被特殊处理。

Shell globbing 和模式匹配是 Shell 语法的一部分。它与引用和其他形式的扩展交织在一起。

$ bash -c 'case "]" in [x"]"]) echo true; esac'
true
Run Code Online (Sandbox Code Playgroud)

引用]删除其特殊含义(关闭前一个[):

当您混合所有内容时,情况可能会更加混乱:

$ ls
*  \*  \a  x

$ p='\*' ksh -xc 'ls $p'
+ ls '\*' '\a'
\*  \a
Run Code Online (Sandbox Code Playgroud)

OK\*是所有以\.

$ p='\*' bash -xc 'ls $p'
+ ls '\*'
\*
Run Code Online (Sandbox Code Playgroud)

并非所有以\. 所以,不知何故,\一定是逃脱了*,但又一次,它也不匹配*......

对于查找,它要简单得多。find在它收到的每个文件参数处下降目录树,然后按照对每个遇到的文件的指示进行测试。

对于-type f,这是真的,如果该文件是一个普通文件,否则为-name <some-pattern>,如果这是真的名字当前考虑的文件的模式相匹配,否则为false。这里没有隐藏文件或/处理或 shell 引用的概念,这只是将字符串(文件名)与模式匹配。

因此,例如,-name '*foo[a/b]ar'(将-name*foo[a/b]ar参数传递给find)将匹配foobar.fooaar。它永远不会 match foo/bar,但那是因为-name匹配文件名;它会用-path代替。

现在,有一种引用/转义的形式 -- forfind -- 在这里被识别,而且只有反斜杠。这允许转义运算符。对于 shell,它是作为通常的 shell 引用的一部分完成的(\是 shell 的引用机制之一)。对于find( fnmatch()),这是模式语法的一部分。

例如,-name '\**'将匹配名称以*. -name '*[\^x]*'将匹配名称包含^x...的文件

现在,作为承认的不同的运营商findfnmatch()bash和其他各种贝壳,他们都应该同意至少在一个共同的子集:*?[...]

特定的 shell 或find实现是使用系统的fnmatch()功能还是它们自己的功能取决于实现。GNUfind至少在 GNU 系统上是这样。Shell 不太可能使用它们,因为这会使它们变得复杂并且不值得付出努力。

bash当然不会。像KSH时,bash,zsh的现代炮弹也有扩展了*?[...]并有多种选择和特殊参数(GLOBIGNORE/ FIGNORE),以影响他们的行为通配符。

另请注意,除了fnmatch()实现 shell 模式匹配之外,还有glob()实现类似于 shell globbing的功能的函数。

现在,这些不同实现中的模式匹配运算符之间可能存在细微差别。

例如,对于 GNU fnmatch(), ?, *or[!x]不会匹配不形成有效字符的字节或字节序列,而bash(和大多数其他外壳)会。例如,在 GNU 系统上,find . -name '*'可能无法匹配名称包含无效字符的文件,而bash -c 'echo *'将列出它们(只要它们不以 开头.)。

我们已经提到了引用可能引起的混乱。