如何在Ubuntu14.04上安装和使用Ack,开发人员的Grep替代品

来自菜鸟教程
跳转至:导航、​搜索

介绍

在文件或目录结构中搜索文本时,Linux 和类 Unix 系统有许多工具可以帮助您。 其中最常见的一种是 grep,它代表全局正则表达式打印。

使用 grep,您可以轻松地在任何文本输入集中搜索可以用正则表达式表达的任何模式。 但是,它不是最快的工具,它是作为通用工具创建的,没有进行任何优化。

为了专门搜索源代码,发明了一种名为 ack 的受 grep 启发的工具。 它利用 Perl 正则表达式来有效地搜索源代码中的模式,同时注意不包含您不关心的结果。

在本指南中,我们将讨论如何使用 ack 作为超级强大的 grep 替代品来从源代码中挑选模式。 ack 工具可在任何可以使用 Perl 的平台上使用,但我们将在 Ubuntu 14.04 服务器上演示该实用程序。

安装确认

要开始使用,第一步是在您的机器上安装 ack 工具。

在 Ubuntu 或 Debian 机器上,这就像从默认存储库安装实用程序一样简单。 该包称为 ack-grep

sudo apt-get update
sudo apt-get install ack-grep

由于可执行文件也安装为 ack-grep,我们可以通过键入以下命令告诉我们的系统将其缩短为 ack,以便我们在命令行中使用该工具:

sudo dpkg-divert --local --divert /usr/bin/ack --rename --add /usr/bin/ack-grep

现在,该工具将响应名称 ack 而不是 ack-grep

如果您打算在其他系统上使用 ack,安装方法可能会有所不同。 CPAN 中的 Perl 模块称为 App::Ack。 在其他 Linux 发行版上,存储库中的软件包名称可能不同。

Ack关注什么

在我们进入 ack 的实际用法之前,让我们先讨论一下它与 grep 的区别以及 ack 范围内的文件。

ack 工具是专门为在程序的源代码中查找文本而创建的。 因此,该工具已经过优化,可以搜索某些文件并忽略其他文件。

例如,如果您正在搜索项目的目录结构,您几乎不会想要搜索版本控制系统的存储库层次结构。 这包含有关旧版本文件的信息,并且可能会导致许多重复。 Ack 意识到这不是您想要搜索的地方,因此它忽略了这些目录。 这会导致更集中的结果,以及更少的误报。

同样,它会忽略某些文本编辑器创建的常见备份文件。 它也不会尝试搜索源目录中常见的非编码文件,例如 Web 文件、图像和 PDF 文件的“缩小”版本等。 所有这些都会为几乎所有搜索带来更好的结果。 您始终可以在执行期间覆盖这些设置。

ack 的另一个特点是它 知道 不同语言的源文件。 您可以要求它在目录结构中查找所有 Python 文件。 它将返回所有以 .py 结尾的文件,但它会 also 返回任何以以下行开头的文件:

 #!  /路径/到/蟒蛇

这将匹配由其扩展名标识的文件以及指示使用通用第一行 魔术数字调用 调用 Python 解释器的文件:

 #!  /路径/到/解释器/到/运行

这创造了一种将非常不同类型的文件归类为相关文件的强大方法。 您还可以根据自己的喜好添加或修改分组。

准备环境

展示 ack 强大功能的最佳方式是在源代码目录中使用它。

幸运的是,我们可以轻松地从 GitHub 等公共站点拉取源代码树。 安装 git 以便我们可以拉下存储库:

sudo apt-get install git

现在,我们需要抓住一个项目。 neovim 项目 就是一个很好的例子,因为它包含许多不同类型的文件。 让我们将该存储库克隆到我们的主目录:

cd ~
git clone https://github.com/neovim/neovim.git

现在,让我们进入该目录开始:

cd neovim

查看不同的文件以了解我们拥有的多样性:

ls

BACKERS.md       CMakeLists.txt   Doxyfile   scripts      uncrustify.cfg
clint-files.txt  config           Makefile   src          vim-license.txt
clint.py         contrib          neovim.rb  test
cmake            CONTRIBUTING.md  README.md  third-party

就在那个顶级目录中,我们看到了 markdown 文件、纯文本、一个 Ruby 文件、一个 Python 文件。 项目的主要部分是用 C 语言编写的。

我们还想设置一些东西来让我们的生活更轻松。

如果结果大于我们的终端窗口,我们希望将输出直接通过管道传输到 less。 这将防止输出不受控制地滚出屏幕。

通过键入:

echo '--pager=less -RFX' >> ~/.ackrc

这将创建我们的 ack 配置文件并添加其第一个非默认选项。 我们告诉它通过管道输出到 less 并带有一些选项,允许它显示彩色输出并智能地处理通道。

使用 Ack 进行简单搜索

让我们开始吧。 首先,让我演示一下 grep 搜索的内容和 ack 搜索的内容之间的区别。

Grep 在目录结构中的每个文件中搜索匹配项。 我们可以通过键入以下内容查看此项目中的文件总数:

find . | wc -l

566

在撰写本文时,neovim 项目中共有 566 个文件。 要找出 ack 关心的文件有多少,我们可以输入:

ack -f | wc -l

497

如您所见,我们已经消除了大约 12% of 个要搜索的文件,甚至没有做任何事情。

假设我们想找出在这个项目中找到模式“restrict”的所有实例。 我们可以输入:

ack restrict

Doxyfile
1851:# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
1860:# code bases. Also note that the size of a graph can be further restricted by
1861:# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.

vim-license.txt
3:I)  There are no restrictions on distributing unmodified copies of Vim except
5:    unmodified parts of Vim, likewise unrestricted except that they must
. . .

如您所见,ack 将“restrict”的实例划分为找到匹配项的文件。 此外,它给出了准确的行号。

但正如您所见,一些实例(我复制的所有示例部分)与“restrict”的变体相匹配,如“restricted”和“restriction”。 如果我们只想要“限制”这个词怎么办?

我们可以使用 -w 标志告诉它搜索被单词边界包围的模式实例。 这将消除该词的其他时态:

ack -w restrict

vim-license.txt
37:       add.  The changes and their license must not restrict others from

clint.py
107:      Specify a number 0-5 to restrict errors to certain verbosity levels.

src/nvim/fileio.c
6846:   * Allow nesting of autocommands, but restrict the depth, because it's
. . .

如您所见,我们的结果现在只显示“限制”,而没有我们之前看到的变化。 输出更加集中。

您可能已经注意到上面我们在不同类型的文件类型中找到的结果。 一种是纯文本文件,一种是在Python文件中找到的,一种是在C源文件中找到的多种情况。

如果我们想告诉 ack 只显示在 Python 文件中找到的结果,我们可以通过键入以下内容轻松地做到这一点:

ack -w --python restrict

clint.py
107:      Specify a number 0-5 to restrict errors to certain verbosity levels.

我们不必指定我们正在寻找的文件模式。 我们不必制作特殊的正则表达式来捕获我们想要的文件类型而不匹配其他文件。 Ack 只知道许多常用语言的文件,您可以通过名称来引用它们。

我们将讨论如何修改每种语言的 ack 返回文件,以及稍后如何定义自己的语言组。

分析我们的搜索焦点

通过添加一些非常简单的标志,我们已经从一个非常广泛的结果变为只有一个。 让我们看看我们究竟缩小了多少范围。

我们可以使用标记 -ch,我们可以将其视为一个简单的成语,意思是“返回了多少匹配项?”。 -c 标志本身告诉 ack 只返回每个文件中匹配行的计数,如下所示:

ack -c restrict

Doxyfile:3
Makefile:0
uncrustify.cfg:0
.travis.yml:0
neovim.rb:0
vim-license.txt:5

这将为每个文件返回一行,即使是那些没有匹配的文件。

-h 标志本身会抑制输出中的文件名前缀并消除结果为零的文件。 他们将一起吐出一个数字,表示搜索匹配的行数:

ack -ch restrict

101

我们从 101 个结果开始。 当我们告诉它注意单词边界时,我们删掉了其中的一大块:

ack -ch -w restrict

16

当然,当我们指定只想查看 Python 文件时,我们将结果缩小到一个匹配项:

ack -ch -w --python restrict

1

我们不仅缩小了搜索范围,而且通过添加语言限制,我们实际上加快了搜索速度。 Ack 不会简单地根据您请求的语言过滤结果,它会在搜索之前执行此操作,以避免搜索不相关的文件。

我们可以通过使用 time 命令定时搜索来看到这一点:

time ack -ch restrict

101

real    0m0.407s
user    0m0.363s
sys     0m0.041s

现在让我们尝试特定语言的子集搜索:

time ack -ch -w --python restrict

1

real    0m0.204s
user    0m0.175s
sys     0m0.028s

第二个明显更快。

修改搜索输出

当我们检查 -c-h 标志时,我们已经讨论过稍微修改搜索输出。 还有其他有用的标志可以帮助我们塑造我们想要的输出。

例如,正如您之前看到的,-c 标志打印出在每个文件中找到匹配模式的行数。 我们之前用 -h 修改它,但我们也可以用 -l 来修改它。 这将只返回找到匹配的文件的数字:

ack -cl restrict

Doxyfile:3
vim-license.txt:5
clint.py:1
test/unit/formatc.lua:1
src/nvim/main.c:4
src/nvim/ex_cmds.c:5
src/nvim/misc1.c:1
. . .

如您所见,所有以“0”结尾的行都已从输出中删除。

如果您想查看 是否在一行中找到匹配项,您可以使用 --column 选项告诉 ack 打印该信息:

 ack -w --column --python 限制
克林特.py  107: 31 :指定数字 0-5 以将错误限制在某些详细级别。 

给出的第二个数字是匹配的第一个字符出现的列号。 一些编辑器允许您转到特定的行和列,这非常有帮助。

例如,如果您使用 vim 文本编辑器打开 client.py 文件,您可以通过键入 107G 进入匹配的确切位置,然后然后 31| 到达列位置。 这种精确定位非常有用,尤其是当您在较大的单词中搜索公共子字符串时。

如果您需要更多的结果上下文,您可以告诉 ack 在匹配发生之前或之后打印出行。 例如,要在 python 文件中打印出“restrict”匹配之前的 5 行,我们可以像这样使用 -B 标志:

 ack -w --python -B 5 限制
 102- output=vs7 103- 默认情况下,输出被格式化以简化 emacs 解析。  也可以使用与 Visual Studio 104 兼容的输出 (vs7)。  不支持其他格式。 105- 106- verbose=# 107:指定数字 0-5 以将错误限制在某些详细级别。 

您可以使用 -A 标志指定匹配后的上下文行数:

 ack -w --python -A 2 限制
 107:指定数字 0-5 以将错误限制在某些详细级别。 108- 109- 过滤器=-x,+y,…

您可以指定通用上下文规范,该规范将使用 -C 标志在匹配的上方和下方打印多行。 例如,要在任一方向获取 3 行上下文,请键入:

 ack -w --python -C 3 限制
也可以使用 104 兼容输出 (vs7)。  不支持其他格式。 105- 106- verbose=# 107:指定数字 0-5 以将错误限制在某些详细级别。 108- 109- filter=-x,+y,… 110- 指定要应用的类别过滤器的逗号分隔列表:仅

要仅打印具有匹配项的文件,而不是打印匹配项本身,您可以使用 -f 标志:

ack -f --python

clint.py
contrib/YouCompleteMe/ycm_extra_conf.py

我们可以做同样的事情,但也可以使用 -g 标志指定文件/目录结构的模式。 例如,我们可以通过键入以下内容来搜索路径中某处具有“log”模式的所有 C 语言文件:

ack -g log --cc

src/nvim/log.h
src/nvim/log.c

使用文件类型

我们已经了解了如何按文件类型过滤的基础知识。 我们可以告诉 ack 只显示 C 语言文件,方法是输入:

ack -f --cc

test/includes/pre/sys/stat.h
src/nvim/log.h
src/nvim/farsi.h
src/nvim/main.c
src/nvim/ex_cmds.c
src/nvim/os/channel.c
src/nvim/os/server.c
. . .

您可以通过键入以下内容查看 ack 知道的所有语言,以及它与每个类别相关联的扩展名和文件属性:

ack --help-types

Usage: ack-grep [OPTION]... PATTERN [FILES OR DIRECTORIES]

The following is the list of filetypes supported by ack-grep.  You can
specify a file type with the --type=TYPE format, or the --TYPE
format.  For example, both --type=perl and --perl work.

Note that some extensions may appear in multiple types.  For example,
.pod files are both Perl and Parrot.

    --[no]actionscript .as .mxml
    --[no]ada          .ada .adb .ads
    --[no]asm          .asm .s
    --[no]asp          .asp
. . .

如您所见,这为您提供了每种文件类型的匹配参数。 您还可以通过在类型前面加上“no”来告诉 ack 排除某个类别的文件。

因此,我们可以通过键入以下内容来查看我们拥有的 C 语言文件的数量:

ack -f --cc | wc -l

191

我们可以通过输入相反的方式查看非 C 语言文件的数量:

ack -f --nocc | wc -l

306

如果我们想修改类型分类怎么办? 例如,如果我们想在查找 CSS 文件时匹配 .sass.scss.less 文件怎么办。 我们可以看到这些已经在类型“sass”和类型“less”类别中匹配,但如果我们愿意,我们也可以将它们添加到 CSS 类别中。

为此,我们可以使用以下通用语法:

 ack --type-add=TYPE:FILTER:ARGS 

--type-add 命令为指定的 TYPE 附加附加匹配规则。 本例中的 FILTERext,表示按文件扩展名匹配。 然后我们可以告诉它我们想要添加这些额外的扩展。

完整的命令如下所示:

ack --type-add=css:ext:sass,scss,less

但是,这仅适用于当前命令(不进行任何搜索)。 我们可以通过键入以下内容来添加搜索:

ack --type-add=css:ext:sass,scss,less -f --css

这将返回任何以 .css.sass.scss.less 结尾的文件。 我们的项目中没有这些文件。 无论哪种方式,这个命令都不是很有用,因为它只存在于当前命令。 您可以通过将其添加到 ~/.ackrc 文件来使其永久化:

echo "--type-add=css:ext:sass,less" >> ~/.ackrc

如果我们想创建一个全新的类型,我们将使用 --type-set 选项。 语法完全相同,唯一的区别是它用于定义不存在的类型。

正如您可能已经收集到的,我们最初的语法规范中的 TYPE 只是类别名称。 我们看到的 FILTER 是文件扩展名,但我们也可以使用其他过滤器。

我们可以使用 is 过滤器直接匹配文件名。 要创建一个名为 example 的类型来匹配名为 example.txt 的文件,我们可以将其添加到我们的 ~/.ackrc 文件中:

--type-set=example:is:example.txt

我们还可以使用 match 过滤器定义与普通正则表达式的匹配。 例如,如果我们想创建一个名为“bashcnf”的类型来匹配“.bashrc”和“.bash_profile”文件,我们可以输入:

echo "--type-set=bashcnf:match:/.bash(rc|_profile)/" >> ~/.ackrc

结论

如您所见,ack 是一个非常灵活的工具,用于处理编程源代码。 即使您只是使用它在 Linux 环境中查找文件,大多数时候,增加的 ack 功能也会很有用。

贾斯汀·艾林伍德