git - 为什么 git grep 中的文件 glob **/*.cs 不显示所有 *.cs 命中?

标签 git grep glob

所以我想在我的项目中找到 NLog 的使用,我使用了 git grep 来帮我做到这一点,但它发现了一些比我需要的情况更多的情况:

git grep NLog
GETA.Seo.Sitemap/Geta.SEO.Sitemaps.csproj:    <Reference Include="NLog, Version=2.1.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
GETA.Seo.Sitemap/Geta.SEO.Sitemaps.csproj:      <HintPath>..\packages\NLog.2.1.0\lib\net45\NLog.dll</HintPath>
GETA.Seo.Sitemap/Services/CloudinaryService.cs:                NLogger.Exception("Could not transform image", exception);
GETA.Seo.Sitemap/Services/CloudinaryService.cs:                NLogger.Warn("Url for cloudinary id was null");
GETA.Seo.Sitemap/Services/CloudinaryService.cs:                NLogger.Warn("Could not locate file object for cloudinary id in EpiServer");
 ....
 etc

当然,它找到了我正在寻找的内容,但我想过滤仅以 .cs 结尾的文件。所以我尝试这样做:

git grep NLog **/*.cs
Web/Global.asax.cs:            NLogger.Info("Meny application start");

只有一次点击,我上面的两场比赛都没有列出。我发现这很奇怪,我可能误解了 git grep 的通配符匹配。有人可以启发我吗?

最佳答案

(术语注释,对于阅读此答案的任何人:扩展诸如 *.cs 之类的内容称为“globbing”,1*.cs 是一个“shell glob”。“shell”是您的命令行解释器,可以是 shbashzsh >dashtcsh 等。Git 有自己内置的通配符。扩展的字符称为通配符,它们包括 *?[。某些 shell 还会特殊对待 {,这在使用 Git 的 reflog 时会出现问题 名称,例如 master@{yesterday}stash@{2}。所有这些名称始终可以引用。)

在这种特殊情况下,问题可能会或可能不会发生在其他人身上,具体取决于他们使用的 shell 和他们的环境 - 是未 protected (未加引号的)* 会经历 shell 通配符。某些 shell,例如 bash,将或者至少可以像 Git 一样扩展 **,这意味着“递归到子目录”。其他人不能,或者根据设置,不会。2

如果您的 shell 扩展 **/*.cs 以包含名称 Web/Global.asax.cs 但不包含 GETA.Seo.Sitemap/Services/CloudinaryService.cs (因为那是目录的下一层),那么当 Git 获取名称时,已经太晚了:通配符 * 字符已经消失了。 Git 永远看不到它们,也无法进行自己的通配。

简单的解决方案是通过引用它们来保护通配符免受 shell 通配符的影响:

git grep '**/*.cs'

(成对的双引号 - 如 git grep "**/*.cs" - 也适用于大多数 shell,并且前缀反斜杠在使用而不是引号时也有效,如 git grep\*\*/\*.cs:只需用反斜杠保护每个易受攻击的字符)。对于许多 Git 命令来说,除非您要 grep 旧的提交,否则对于 git grep 来说并不那么重要,最好始终保护所有通配符,以便它们传递到 Git,因为 Git将针对当前工作树以外的其他内容扩展它们。 shell 只能看到工作树。3)

虽然它依赖于 shell,但有时通配符会匹配什么,然后被传递。例如,如果您没有名为 sub 的目录,并且您编写了 sub/*,则某些(不是全部)shell 将传递文字文本 sub/* 到您运行的命令。4 在这种情况下,如果该命令是 Git 命令,它可以再次执行自己的通配。依赖于此并不明智,因为一旦有东西要匹配,shell 就会进行匹配,而不是将原始通配符传递给程序。


1名称“glob”是“global”的缩写,在非常早期的 shell 中,是由名为 glob 的外部程序完成的。 Early versions of Unix ran on machines with as little as 64 kilobytes of memory ,所以没有太多的空间用于花哨的壳内扩展。请参阅https://en.wikipedia.org/wiki/Glob_(programming)了解更多。

2在 bash 中,Git 风格的扩展是通过设置变量 globstar 来控制的。

3这甚至可能包括 .git 存储库子目录本身,这通常是不好的。在 bash 中,这是由变量 dotglob 控制的。

4在 bash 中,这是由 failglob 控制的。

请注意,bash 几乎提供了每种可能的 shell 的所有可能行为。它试图成为一种通用的外壳。当然,这意味着它也需要所有这些控制变量,这使得 bash 相当大。您永远无法在 64K 非拆分 I&D PDP-11 上运行它。

关于git - 为什么 git grep 中的文件 glob **/*.cs 不显示所有 *.cs 命中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40659139/

相关文章:

git - 如何确定 git 存储库的父存储库?

python - pip installrequirements.txt 无法正常工作

git - git可以 merge 两个文件而忽略它们的共同祖先吗

linux - 匹配 Bash 变量中的第一个子字符串

gruntjs - Gruntfile 中的简单正则表达式

git - Bitbucket gnutls_handshake() 失败 : Error in the pull function

linux - 从文件中grep两个字符串并写入txt

string - 替换文件中字符串的命令

python - 在Python中删除具有不同名称的文件

python - 将相同形状的动态图像读取到Python NumPy数组中