Bash 模式通过“显式”来匹配名称以点(句点)开头的目录,而不是使用“shopt -s dotglob”?

Bash 模式通过“显式”来匹配名称以点(句点)开头的目录,而不是使用“shopt -s dotglob”?

在构造与文件名(例如 )匹配的模式时/home/user/project/.git,如何.“显式”匹配该字符——即不使用shopt -s dotglob

手册位于https://www.gnu.org/software/bash/manual/html_node/Filename-Expansion.html状态:

当模式用于文件名扩展时,字符“.”文件名开头或紧跟在斜杠后面的字符必须显式匹配,除非设置了 shell 选项 dotglob。

“明确匹配”到底是什么意思?

再说一次,在http://www.tldp.org/LDP/abs/html/globbingref.html(在Notes最后的部分),解决了相同的概念:

文件名扩展可以匹配点文件,但前提是模式明确包含点作为文字字符。

该注释提供了以下示例:

~/[.]bashrc    #  Will not expand to ~/.bashrc
~/?bashrc      #  Neither will this.
               #  Wild cards and metacharacters will NOT
               #+ expand to a dot in globbing.

~/.[b]ashrc    #  Will expand to ~/.bashrc
~/.ba?hrc      #  Likewise.
~/.bashr*      #  Likewise.

我无法理解最后三个示例的内部工作原理,这些示例将扩展为包含“点文件”。

具体来说,如何在示例中将放在b括号中使其成为“显式”匹配?接下来的例子对我来说更加模糊。我只是无法理解以看似与字符完全无关的方式操作模式如何导致模式产生匹配。.~/.[b]ashrc.

关于为什么我想避免使用shopt -s dotglob,这个问题的推动力源于我正在编写这些模式以在另一个程序的配置文件中使用的事实。我想排除包含“隐藏目录”等的路径.git,并且我不确定我是否有能力dotglob以任何身份指定。

.本质上:通过“明确”来匹配角色的最简单方法是什么?将下一个字符放在括号中“使其起作用”,但我想知道为什么;我觉得我用这种方法是在“在黑暗中拍摄”。

非常感谢任何有关这方面潜在行为的解释。

编辑添加:

最初,它似乎并不相关,但因为人们似乎对我的用例的具体细节感兴趣,所以我将进一步解释。

我正在使用名为 的基于主机的入侵检测软件Samhain。只要文件系统根据某些用户指定的配置参数被修改,Samhain 就会“发出警报”。

.git希望 Samhain 在目录(位于某些父目录中)内的文件被创建/修改/删除时发出警报。在 Samhain 中,这种类型的排除是通过定义“忽略规则”来执行的。这些规则的具体规范解释于http://www.la-samhna.de/samhain/manual/filedef.html, 在4.2. File/directory specification

简而言之:

Wildcard patterns ('*', '?', '[...]') as in shell globbing are supported for paths. The leading '/' is mandatory.

因此,我试图编写一个“忽略规则”来匹配.git相关目录,这实际上会导致 Samhain 将它们排除在其监视活动之外。

最初,我尝试了这个:

[IgnoreAll]
dir = -1/home/user/project/*/*/.git

这不起作用;每当这些目录中的文件发生更改时,Samhain 仍然会发出警报.git

找到上面引用的例子后,我尝试了以下方法:

dir = -1/home/user/project/*/*/.[g]it

通过此更改,Samhain 根据需要忽略这些文件。

在发布这个问题时,我只是想理解为什么这种变化会产生预期的效果。

我会说,考虑到当我使用“echo”测试时我最初尝试使用的模式确实与.git有问题的目录匹配,我感觉不那么愚蠢了:

echo /home/user/project/*/*/.git

所以,这并不是说我误解了 Bash 中模式匹配、通配符或文件名扩展的一些基本知识;而是我误解了 Bash 中的模式匹配、通配符或文件名扩展等基本知识。相反,Samhain 在这种情况下如何实现模式匹配似乎存在细微差别。

我不知道为什么这在 Samhain 的配置文件上下文中应用时不起作用(显然)。鉴于此编辑,也许有人能够解释。

答案1

当模式用于文件名扩展时,字符“.”文件名开头或紧跟在斜杠后面的字符必须显式匹配,除非设置了 shell 选项 dotglob。

这仅仅意味着 glob *?和与文件名开头的[...]a 不匹配。.如果要匹配.文件名开头的 a,则不能使用 glob,必须.显式键入。例如:

$ echo ????
Work
$ echo .???
.gem .pki .ssh .vim

并回答你的另一个问题:

具体来说,如何在示例中将放在b括号中使其成为“显式”匹配?.~/.[b]ashrc

仅仅因为您使用的是 glob 模式并不意味着所有的模式不再是“明确的”。例如,在 中~/.[b]ashrc,字符/.ashrc都是显式匹配的。但是,[b]它是一个全局模式,也不是显式匹配。 (从技术上讲,~是波浪线扩展并且早于全局扩展执行,因此它也是显式匹配。)但是其他字符,包括.,显式匹配,这就是~/.[b]ashrcmatches 的原因~/.bashrc

为了比较,~/?[b]ashrc不是match ~/.bashrc,因为.不再显式匹配。

答案2

首先,我假设您知道路径名模式中[b]?和等内容的含义。 *(如果不这样做,请进行更多研究。)

冒着重复别人说过的话的风险,你想得太多了。包含字符串的模式/. (即/ 立即地随后是一个.) 明确包含点作为文字字符。重点是[b]?和/或*正在发生. 不影响模式是否可以匹配点文件。最后三个示例作为示例提供图案 (即,不仅仅是一个普通的文件/路径名,而是可能匹配多个文件/路径名的东西 - 或没有)将匹配~/.bashrc- 与前两个相反,匹配~/.bashrc如果.没有经过特殊处理。

那么,您真正的问题是什么?

...我正在编写这些模式以在另一个程序的配置文件中使用。我想排除包含“隐藏目录”等的路径.git,并且我不确定我是否有能力dotglob以任何身份指定。

我猜你想对所有文件/目录执行某些操作(例如chown或 )cp除了以点开头的。但是您的代码将在其他人的脚本中使用(通过.source命令),并且您害怕这样做,your_command * 因为脚本可能已设置dotglob,因此*会扩展到所有文件,包括“隐藏”文件。而且您不想关闭,dotglob 因为您不想破坏现有脚本的功能。

  1. 使用更智能的通配符(路径名扩展模式)。

    我希望您了解通配符(又名通配符),例如[abc]它们匹配任何字符a,bc。例如,字符串c[aou]t匹配cat,cotcut;  d[iou]g匹配dig,dogdug. (它们可以并且通常与范围一起使用;例如,[a-z][0-9]。)嗯,一个特殊情况是- 它匹配任何字符[!abc]除了 ab或者c。因此,您可以使用[!.]*(或directory_name/[!.]*) 来匹配以点以外的字符开头的名称。矛盾的是,如果未设置,[.](在文件名的开头)将不会匹配点,但会dotglob[!.]排除一个点,无论 的设置如何 dotglob

    dotglob无论设置与否,这都会给出相同的结果。

  2. 使用dotglob(在子 shell 中)。

    Shell 选项shopt是进程的本地选项,并且进程属性永远不会从子进程向后(上坡)流动到父进程。所以

    (shopt -u dotglob;你的命令*)
    会跑your_command仅适用于非隐藏文件,而不影响脚本其余部分的设置和行为。

  3. 使用dotglob(不使用子shell)。

    有些人更喜欢避免使用子 shell,因为它们会使用额外的资源。但成本是微乎其微的(除非你在执行的循环中执行此操作)许多次),所以这不是一个很好的理由。避免使用子 shell 的一个更好的理由是,如果您需要执行一些影响 shell 环境的操作,例如cdumask

    如果您遇到这种情况,可以暂时关闭dotglob,稍后再恢复之前的设置。

    如果您键入shopt dotglob(不带-s-u),它将报告(显示)该选项的当前设置dotglob。 (shopt参数列出了当前设置全部选项。)它还相应地设置退出状态。该-q标志抑制显示,所以你可以这样做

    shopt -q 点球
    点glob_setting=$?
    shopt -u 点glob
    你的命令*
    如果 [“$dotglob_setting”= 0]
    然后
        shopt -s 点球

可是等等 …你说的是“另一个程序的配置文件”。你在说什么?如果您正在谈论编写或修改类似的文件ignore=*.o,那么整个问题就没有意义,因为该文件将由处理它的任何程序处理(和解释),并且那个程序将决定如何解释*——shell 与它无关。


好的,现在我们对问题是什么有了更好的了解:

简而言之,您所看到的行为没有意义。如果.git目录存在,则将其精确(字面)指定为.git 并使用通配符/全局模式指定它.[g]it 应具有相同的行为。

较长的答案:我支持我的答案第一个版本的最后一段。 Samhain 正在读取并解析其策略配置文件。它可能使用 shell 来解释配置文件中的通配符,但我猜它是在内部执行的。

而且,如果是“使用 shell”,那么它使用的是哪个 shell?在许多系统上,/bin/sh不是 bash。它们在路径名扩展模式(即通配符)方面的基线行为应该是相同的,但是一旦你走出门廊,你就陷入了沼泽。 shell 的 POSIX 规范甚至没有命令shopt,并且(据我所知)没有任何方法可以*扩展为全部文件(不仅仅是非隐藏文件)。

如果你觉得浪费在这方面花费更多时间,您可以尝试放入  /home/user/project/*Samhain 配置文件,看看它是否将其解释为所有文件或只是非隐藏文件。如果它将其解释为所有文件,我们可以得出结论

  1. Samhain 不使用/bin/sh扩展通配符。
  2. 它没有使用通配符的标准默认规则(您在问题中详细讨论的规则)。
  3. 该文档是错误的(或者充其量是不完整和误导性的),因为它说:“路径支持 shell 通配符中的通配符模式('*'、'?'、'[...]')。”不用说(与 shell 的默认行为不同)*意味着所有文件。
  4. 可能在模式下使用 bashdotglob来扩展通配符。但这没有意义;正如我所说,.git和的处理.[g]it 与我所知道的任何 shell 的正常行为都不相符。几乎可以肯定它有自己的通配符代码。

但无论如何,我相信我们可以有信心地说你的结论是正确的:Samhain 在IgnoreAll规范中通配符的处理方面存在错误。您可能想向供应商提交错误报告。或者,既然您已经找到了解决方法,您就可以忘记它。

相关内容