10 个实用的 Grep 命令示例

最近,我开始与 Asciidoctor.js 并且在 Asciidoctor.js-哈巴狗Asciidoctor-templates.js 项目。

当您第一次挖掘包含数千行代码的代码库时,并不总是那么容易立即生效。 但是我在这么多代码行中找到出路的秘密武器是 grep 工具。

我将通过示例与您分享如何在 Linux 中使用 grep 命令。

Linux 中 grep 命令的真实有用示例

如果您查看 man,您将看到 grep 工具:“打印与模式匹配的行。”

但是,不要被这样谦虚的定义所迷惑: grep 是 Unix 工具箱中最有用的工具之一,一旦您处理文本文件,就会有无数次使用它。

最好有真实世界的例子来了解事情是如何运作的。 所以,我将使用 Asciidoctor.js 源代码树 来说明一些 grep 能力。

您可以从 GitHub 下载该源代码树,如果需要,您甚至可以查看我在撰写本文时使用的相同变更集。 这将确保您获得与本文其余部分所述的结果完全相同的结果:

git clone https://github.com/asciidoctor/asciidoctor.js
cd asciidoctor.js
git checkout v1.5.6-rc.1

1.查找所有出现的字符串(基本用法)

Asciidoctor.js 支持 Nashorn JavaScript 引擎 用于 Java 平台。 我不了解 Nashorn,因此我可以借此机会通过探索引用该 JavaScript 引擎的项目部分来了解更多有关它的信息。

作为起点,我检查了是否有一些与 Nashorn 相关的设置 package.json 描述项目依赖项的文件:

[email protected]:~$ grep nashorn package.json
    "test": "node npm/test/builder.js && node npm/test/unsupported-features.js && node npm/test/jasmine-browser.js && node npm/test/jasmine-browser-min.js && node npm/test/jasmine-node.js && node npm/test/jasmine-webpack.js && npm run test:karmaBrowserify && npm run test:karmaRequirejs && node npm/test/nashorn.js",

是的,显然有一些针对 Nashorn 的测试。 所以,让我们再调查一下。

2. 文件集中不区分大小写的搜索

现在,我想仔细查看来自 ./npm/test/ 目录明确提到 Nashorn。

不区分大小写的搜索 (-i 选项)在这里可能更好,因为我需要找到两个参考 nashornNashorn (或任何其他大小写字符组合):

[email protected]:~$ grep -i nashorn npm/test/*.js
npm/test/nashorn.js:const nashornModule = require('../module/nashorn');
npm/test/nashorn.js:log.task('Nashorn');
npm/test/nashorn.js:nashornModule.nashornRun('jdk1.8.0');

实际上,不区分大小写在这里很有用。 否则,我会错过 require('../module/nashorn') 陈述。 毫无疑问,我稍后应该更详细地检查该文件。

3.查找所有不匹配的文件

顺便说一句,是否有一些非 Nashorm 特定文件 npm/test/ 目录? 要回答这个问题,我们可以使用 grep 的“打印不匹配文件”选项(-L 选项):

sh$ grep -iL nashorn npm/test/*
npm/test/builder.js
npm/test/jasmine-browser-min.js
npm/test/jasmine-browser.js
npm/test/jasmine-node.js
npm/test/jasmine-webpack.js
npm/test/unsupported-features.js

注意如何使用 -L 选择输出 grep 已更改为仅显示文件名。 因此,以上文件均不包含字符串“nashorn”(无论大小写如何)。 这并不意味着它们与该技术没有某种关系,但至少,字母“nashorn”不存在。

4. 在隐藏文件中查找模式并递归到子目录中

最后两个命令使用了 shell 球状图案 将要检查的文件列表传递给 grep 命令。

但是,这有一些固有的局限性:星 (*) 将不匹配隐藏文件。 它也不会匹配子目录中包含的文件(最终)。

一个解决方案是结合 grep 使用 find 命令而不是依赖于 shell glob 模式:

# This is not efficient as it will spawn a new grep process for each file
[email protected]:~$ find npm/test/ -type f -exec grep -iL nashorn {} ;
# This may have issues with filenames containing space-like characters
[email protected]:~$ grep -iL nashorn $(find npm/test/ -type f)

正如我在上面代码块的注释中提到的那样,这些解决方案中的每一个都有缺点。

关于包含类似空格字符的文件名,我让您调查 grep -z 选项,结合 -print0 的选项 find 命令,可以缓解这个问题。 不要犹豫,使用本文末尾的评论部分来分享您对该主题的想法!

然而,更好的解决方案是使用“递归”(-r) 选项 grep. 使用该选项,您可以在命令行上提供搜索树的根目录(起始目录),而不是要检查的文件名的显式列表。

随着 -r 选项,grep 将搜索指定目录中的所有文件,包括隐藏文件,然后递归下降到任何子目录:

[email protected]:~$ grep -irL nashorn npm/test/npm/
npm/test/builder.js
npm/test/jasmine-browser-min.js
npm/test/jasmine-browser.js
npm/test/jasmine-node.js
npm/test/jasmine-webpack.js
npm/test/unsupported-features.js

实际上,使用该选项,我还可以在上一级开始我的探索,以查看也有针对 Nashorn 的非 npm 测试:

[email protected]:~$ grep -irL nashorn npm/

我让您自己测试该命令以查看其结果; 但作为一个提示,我可以说你应该找到更多匹配的文件!

5. 按名称过滤文件(使用正则表达式)

因此,该项目中似乎有一些特定于 Nashorn 的测试。 由于 Nashorn 是 Java,因此可能会提出另一个问题是“项目中是否有一些 Java 源文件明确提到了 Nashorn?”。

根据版本 grep 你使用,至少有两种解决方案可以回答这个问题。

第一个是使用 grep 查找包含模式“nashorn”的所有文件,然后将第一个命令的输出通过管道传输到第二个 grep 过滤掉非java源文件的实例:

[email protected]:~$ grep -ir nashorn ./ | grep "^[^:]*.java"
./spec/nashorn/AsciidoctorConvertWithNashorn.java:public class AsciidoctorConvertWithNashorn {
./spec/nashorn/AsciidoctorConvertWithNashorn.java:    ScriptEngine engine = engineManager.getEngineByName("nashorn");
./spec/nashorn/AsciidoctorConvertWithNashorn.java:    engine.eval(new FileReader("./spec/nashorn/asciidoctor-convert.js"));
./spec/nashorn/BasicJavascriptWithNashorn.java:public class BasicJavascriptWithNashorn {
./spec/nashorn/BasicJavascriptWithNashorn.java:    ScriptEngine engine = engineManager.getEngineByName("nashorn");
./spec/nashorn/BasicJavascriptWithNashorn.java:    engine.eval(new FileReader("./spec/nashorn/basic.js"));

该命令的前半部分现在应该可以理解了。 但是那个“^[^:]*.java” 部分?

除非您指定 -F 选项, grep 假设搜索模式是 正则表达式. 这意味着,除了将逐字匹配的普通字符之外,您还可以访问一组元字符来描述更复杂的模式。 我上面使用的模式只会匹配:

  • ^ 行的开始
  • [^:]* 后跟除冒号以外的任何字符序列
  • . 后跟一个点(点在正则表达式中具有特殊含义,因此我必须用反斜杠保护它以表示我想要文字匹配)
  • java 然后是四个字母“java”。

在实践中,由于 grep 将使用冒号将文件名与上下文分开,我只保留具有 .java 在文件名部分。 值得一提的是它也会匹配 .javascript 文件名。 如果你愿意,这是我让你自己尝试解决的问题。

6. 使用 grep 按名称过滤文件

正则表达式非常强大。 但是,在这种特殊情况下,这似乎有点过分了。 更不用说上面的解决方案了,我们花时间检查所有文件以寻找“nashorn”模式——大部分结果都被管道的第二步丢弃了。

如果您使用的是 GNU 版本的 grep,如果您使用的是 Linux,那么您可能会遇到另一种解决方案 --include 选项。 这指示 grep 仅搜索名称与给定 glob 模式匹配的文件:

[email protected]:~$ grep -ir nashorn ./ --include="*.java"
./spec/nashorn/AsciidoctorConvertWithNashorn.java:public class AsciidoctorConvertWithNashorn {
./spec/nashorn/AsciidoctorConvertWithNashorn.java:    ScriptEngine engine = engineManager.getEngineByName("nashorn");
./spec/nashorn/AsciidoctorConvertWithNashorn.java:    engine.eval(new FileReader("./spec/nashorn/asciidoctor-convert.js"));
./spec/nashorn/BasicJavascriptWithNashorn.java:public class BasicJavascriptWithNashorn {
./spec/nashorn/BasicJavascriptWithNashorn.java:    ScriptEngine engine = engineManager.getEngineByName("nashorn");
./spec/nashorn/BasicJavascriptWithNashorn.java:    engine.eval(new FileReader("./spec/nashorn/basic.js"));

7. 找词

Asciidoctor.js 项目的有趣之处在于它是一个多语言项目。 Asciidoctor 的核心是用 Ruby 编写的,因此,为了在 JavaScript 世界中可用,它必须使用“转译” 蛋白石,一个 Ruby 到 JavaScript 的源到源编译器。 另一项我以前不知道的技术。

因此,在检查了 Nashorn 的特性之后,我给自己分配了更好地理解 Opal API 的任务。 作为该任务的第一步,我搜索了所有提及 Opal 项目的 JavaScript 文件中的全局对象。 它可能出现在做作(Opal =), 会员访问 (Opal.) 或者甚至在其他情况下。 正则表达式可以解决问题。 然而,再一次, grep 有一些更轻量级的解决方案来解决该常见用例。 使用 -w 选项,它将仅匹配单词,即前面和后面跟着非单词字符的模式。 非单词字符要么是行首、行尾,要么是任何既不是字母、也不是数字、也不是下划线的字符:

[email protected]:~$ grep -irw --include="*.js" Opal .
...

8. 着色输出

我没有复制上一个命令的输出,因为有很多匹配项。 当输出像这样密集时,您可能希望添加一点颜色以方便理解。 如果您的系统上尚未默认配置此功能,您可以使用 GNU 激活该功能 --color 选项:

[email protected]:~$ grep -irw --color=auto --include="*.js" Opal .
...

您应该获得与以前相同的长结果,但这次搜索字符串应该以彩色显示(如果不是这样的话)。

9. 统计匹配行或匹配文件

我两次提到前面命令的输出很长。 具体多久?

[email protected]:~$ grep -irw --include="*.js" Opal . | wc -l
86

这意味着我们在所有检查的文件中总共有 86 行匹配。 但是,有多少不同的文件匹配? 随着 -l 选项你可以限制 grep 输出匹配的文件而不是显示匹配的行。 所以这个简单的改变将告诉有多少文件是匹配的:

[email protected]:~$ grep -irwl --include="*.js" Opal . | wc -l
20

如果这让你想起 -L 选项,不足为奇:因为它比较常见,小写/大写用于区分互补选项。 -l 显示匹配的文件名。 -L 显示不匹配的文件名。 为了另一个 example,我让你检查手册 -h/-H 选项。

让我们 close 那个括号并回到我们的结果:86 匹配行。 20 个匹配文件。 但是,匹配文件中的匹配行是如何分布的呢? 我们可以知道,使用 -c 选项 grep 这将计算每个检查文件的匹配行数(包括零匹配的文件):

[email protected]:~$ grep -irwc --include="*.js" Opal .
...

通常,该输出需要一些后处理,因为它以检查文件的顺序显示其结果,并且它还包括没有任何匹配的文件——这通常是我们不感兴趣的。 后者很容易解决:

[email protected]:~$ grep -irwc --include="*.js" Opal . | grep -v ':0$'

至于排序,您可以在管道末尾添加排序命令:

[email protected]:~$ grep -irwc --include="*.js" Opal . | grep -v ':0$' | sort -t: -k2n

我让你检查 sort 我使用的选项的确切含义的命令手册。 不要忘记使用下面的评论部分分享您的发现!

10.找出两个匹配集之间的差异

如果你还记得,在几个命令之前,我搜索了“Opal”这个词。 但是,如果我在同一个文件集中搜索所有出现的字符串“Opal”,我会得到大约 20 个以上的答案:

[email protected]:~$ grep -irw --include="*.js" Opal . | wc -l
86
[email protected]:~$ grep -ir --include="*.js" Opal . | wc -l
105

找出这两组之间的差异会很有趣。 那么,连续包含四个字母“opal”的行是什么,但是这四个字母不构成一个完整的单词呢?

回答这个问题并不容易。 因为同一行可以包含 Opal 一词以及包含这四个字母的一些较大的词。 但作为第一个近似值,您可以使用该管道:

[email protected]:~$ grep -ir --include="*.js" Opal . | grep -ivw Opal
./npm/examples.js:  const opalBuilder = OpalBuilder.create();
./npm/examples.js:  opalBuilder.appendPaths('build/asciidoctor/lib');
./npm/examples.js:  opalBuilder.appendPaths('lib');
...

显然,我的下一站是调查 opalBuilder 反对,但这将是另一天。

最后一个字

当然,你不会仅仅通过发布几个项目来了解项目组织,更不用说代码架构 grep 命令!

但是,我发现在探索新代码库时,该命令不可避免地会识别基准和起点。

所以,我希望这篇文章能帮助你了解 grep 命令,您将把它添加到您的工具箱中。 毫无疑问你不会后悔的!