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

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

对于文件名扩展,“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 库或其他一些机制来进行文件名扩展和模式匹配?

答案1

在shell中,需要区分文件名生成/扩展(又名通配:扩展为文件列表的模式)模式匹配通配用途内部模式匹配,但实际上首先是操作员产生基于的文件列表图案

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

case $file in
  */*.txt) echo match
esac

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

然而,*/*.txt作为一个全局是相关但更复杂的东西。

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

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

类似地,glob like将查找目录中foo[a/b]baz*名称以 开头的所有文件。b]bazfoo[a

因此,我们已经看到,对于通配符而言,但对于模式匹配而言,/这是特殊的(通配符以某种方式分割/,并且每个部分单独处理)并且点文件被特殊处理。

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

$ bash -c 'case "]" in [x"]"]) echo true; esac'
true

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

当你混合所有东西时,它可能会更加混乱:

$ ls
*  \*  \a  x

$ p='\*' ksh -xc 'ls $p'
+ ls '\*' '\a'
\*  \a

OK\*是所有以\.

$ p='\*' bash -xc 'ls $p'
+ ls '\*'
\*

并不是所有以\.所以,不知何故,\一定已经逃脱了*,但话又说回来,它也不匹配*......

对于find来说,就简单多了。find在它收到的每个文件参数处沿着目录树下降,然后按照指示对每个遇到的文件进行测试。

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

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

现在,有一种引用/转义的形式——为了find-- 此处已识别,并且仅带有反斜杠。这允许逃避操作员。对于 shell,它是作为通常 shell 引用的一部分完成的(\是 shell 的引用机制之一)。对于find( fnmatch()),这是模式语法的一部分。

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

现在,对于find、和各种fnmatch()其他bashshell 识别的不同运算符,它们至少应该在一个公共子集上达成一致:*?[...]

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

bash当然不是。像 ksh、bash、zsh 这样的现代 shell 还对*?[...]以及许多选项和特殊参数 ( GLOBIGNORE/ FIGNORE) 进行了扩展,以影响它们的通配行为。

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

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

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

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

相关内容