发现一个比ack更快更好用的: https://github.com/ggreer/the_silver_searcher , 使用时命令为ag,它是基于ack的代码二次开发的,所有使用方法基本和ack差不多(包括命令行参数),但也小有区别。
ubuntu 下安装: sudo apt-get install silversearcher-ag
ag man page: https://www.mankier.com/1/ag
可以在 源码(要搜索的目录) 目录建立一个 .ignore 文件来让ag忽略不想搜索的文件,如:
$ cat ~/cloudkick/reach/.ignore: *cough* *.min.js
ag example:
ag printf : Find matches for "printf" in the current directory.
ag foo /bar/ : Find matches for "foo" in path /bar/.
ag -- --foo : Find matches for "--foo" in the current directory. (As with most UNIX command line utilities, "--" is used to signify that the remaining arguments should not be treated as options.)
ag -G cpio.c size : 在所有文件名为 *cpio.c* 的文件中搜索字串 size. (
-G --file-search-regex PATTERN,
Only search files whose names match PATTERN. 帮助中出现PATTERN项就代表必须使用正则式)
ag -G cpio.c -w size : 在所有文件名为 *cpio.c* 的文件中搜索单词 size.
ag readme$ : 正则式搜索 readme$
ag .rb files/ : 在files目录搜索含 .rb 的字串 (所有的 arb, crb 等等)
ag -Q .rb files/ : 在files目录搜索含 .rb 的字串 (只匹配 .rb,相当于正则式的 \.rb)
ag DHH -l : 搜索含有单词 DHH 的所有文件,且只打印处文件名
ag readme -l -G action : 在所有文件名为 *action* 的文件中搜索字串 readme,且只打印处文件名
ag readme -l -G action$ : 在所有文件名为 *action 的文件中搜索字串 readme,且只打印处文件名
ps -e | ag forego : 通过管道使用 ag
ag readme -l -G action$ --pager "less -R" : 在所有文件名为 *action 的文件中搜索字串 readme,且只打印处文件名,使用 less来分页。(
alias ag="ag $* --pager 'less -R'",这个可以一劳永逸
)
下面是 grep, ack, ag 的对比:
grep, ack, or ag, which one should you use?
There’s no wrong answer. Each tool has its own strengths, and you should use whichever best fits your needs. This quick cheat sheet can help inform your decision:
grep
- Available on all Unix-like systems by default, but not on Windows.
- Everyone knows it, and should be used for scripting purposes.
ack
- Very portable, runs on any system that runs Perl, including Windows
- Ignores backup files, binary files, your VCS’s work files, and other unwanteds
- True Perl regular expressions, not PCRE, because it’s written in Perl
- Flexible output with the
--output
option - User-definable file types
- Project-level configuration
ag
- Very fast
- Uses your VCS’s ignore files to know what to ignore
- Searches compressed files
- Better editor integration
- Not as portable; Windows version is out of date
可以看出,ag无法自定义文件类型,即 ack 的 --type-set 选项。
ubuntu下要安装ack-grep,因为在debian系中,ack这个名字被其他的软件占用了。 sudo apt-get install ack-grep。
什么是ACK?
- http://betterthangrep.com
- ack is a tool like grep, optimized for programmers
- is written purely in Perl 5,takes advantage of the power of Perl's regular expressions.
- 作者在厌烦了不停的写下面的这个查找命令之后,开发了ack这个工具
grep foo $(find . -name '*.pm' | grep -v .svn)
可以看的出来,ack诞生的目的就是要取代grep,从作者开发的初衷以及它官网的名字,另外它还有一个“可以替代99%grep的工作”这个口号。
ack 的 github地址: https://github.com/petdance/ack2
ack 的 vim plugin 的地址: https://github.com/mileszs/ack.vim
ack 的 官方帮助: https://beyondgrep.com/documentation/ack-2.18-man.html
安装
ubuntu下要安装ack-grep,因为在debian系中,ack这个名字被其他的软件占用了,使用 sudo apt-get install ack-grep.
如果你的程序安装完成之后名字叫 ack-grep,是不是会嫌弃使用太麻烦了,可以这ubuntu下用下面的命令将它改成 ack:
$ sudo dpkg-divert --local --divert /usr/bin/ack --rename --add /usr/bin/ack-grep
安装完后,执行:
$ echo "--type-add=html:ext:shtml,xhtml" >> ~/.ackrc
将shtml和xhtml加入到html类型里。注意,ack每次执行命令的时候,会将 ~/.ackrc 里的参数都自动加到你这终端执行的命令里。
特点
大家都说自己的东西好,因此ack官网列出了这工具的5大卖点:
- 速度非常快,因为它只搜索有意义的东西。
- 更友好的搜索,忽略那些不是你源码的东西。
- 为源代码搜索而设计,用更少的击键完成任务。
- 非常轻便,移植性好。
- 免费且开源
better than grep?
先来看下grep的日常用法。
- grep常用操作
-
- grep -r 'hello_world' # 简单用法
- grep '^hello_world' . # 简单正则
- ls -l | grep .py # 管道用法
一些参数:
-c(统记)/ -i(忽略大小)/ -h(不显示名称)/ -l(只显文件名)/ -n(加行号)/ -v(显示不匹配)
这些命令在Linux上的适用频率是相当高的,尤其是你用vim做为IDE的话(当然这是说在不知道ack之前)。
ack功能划分
在记忆的时候大体上可以分为这几个部分:
- Searching代码搜索
- Search output搜索结果处理
- File presentation文件展示
- File finding文件查找
- File inclusion/exclusion文件过滤
下面对每一项给几个简单实用的例子。
基本用法:
- -i 不区分大小写
- -f --X only prints the files that would be searched, without actually doing any searching, where “X” denotes the filetype (e.g., “--html”)
- -n does not descend into any subdirectories. 不进入子目录进行递归搜索。
- -w only matches whole words。 搜索整个单词,而不是字符匹配。
- --type=noX excludes certain filetypes from the search, where “X” denotes the filetype to be excluded (e.g., “--type=nophp” to exclude PHP files)
Searching
简单的文本搜索,默认是递归的。
ack --help-types # 打印所有type类型
ack "include|RTSP" . #ack 'pattern1|pattern2|pattern3'
可以搜索含有pattern1 或者 pattern2 或者 pattern3 的行
ack-grep hello ack-grep -i hello ack-grep -v hello # 反向选择,即过滤掉含 hello 的行 ack-grep -w hello ack-grep -Q 'hello*' ack -f --html # 打印类型是html的文件,不做具体的搜索,相当于find命令,html类型的文件大致包含: .html, .htm. 使用 ack -f --html -w index 方式是不对的,不能同时搜文件和文件内容。 ack -w --type=nojs css #查找不是js类型之外的其它文件,只要该文件含有单词css ack -n "(^9999)|(^000)" #这种正则表达式和grep的类似,这里只是查找文件,不进入子目录进行递归(-n),它显示含有以9999或者000开头的行 ack -g log --cc # -g代表只搜索文件(会递归进子目录),这里的意思是搜索c类型的文件,其文件名相对路径含有字符log,输入如 *log*.c *log*.h 这样的文件。 ack -g log --cc -w aa,这样使用是不对的,不能同时搜文件和文件内容。
ack --type-add=html:ext:shtml,xhtml #只影响当前的命令,必须配合搜索命令一起使用,如:ack --type-add=html:ext:shtml,xhtml --html -w body,或者 ack --type-add=html:ext:shtml,xhtml -f --html echo "--type-add=html:ext:shtml,xhtml" >> ~/.ackrc #这个可以将增加某类型的类型定义保存起来,以后每次使用ack都会生效。 --type-set=example:is:example.txt # 这个是改变或新建某类型的类型文件。 echo "--type-set=bashcnf:match:/.bash(rc|_profile)/" >> ~/.ackrc #这个是用正则式来定义类型参数,定义里一个叫bashcnf的类型,该类型匹配 .bashrc 或 .bash_profile 两个文件。 ack 'string1|string2' #搜索string1或string2.
Search File
对搜索结果进行处理,比如只显示一个文件的一个匹配项,或者xxx
ack-grep --line=1 # 输出所有文件第二行 ack-grep -l 'hello' # 搜索内容包含hello的所有文件,但是只打印其文件名,不打印匹配的文件内容行。 ack-grep -L 'print' # 搜索内容不包含print的所有文件,但是只打印其文件名,不打印匹配的文件内容行。
File presentation
输出的结果是以什么方式展示呢,这个部分有几个参数可以练习下
ack-grep hello --pager='less -R' # 以less形式展示 ack-grep hello --noheading # 不在头上显示文件名 ack-grep hello --nocolor # 不对匹配字符着色
File finding
没错,它可以查找文件,以省去你要不断的结合find和grep的麻烦,虽然在linux的思想是一个工具做好一件事。
ack-grep -f hello.py # 查找全匹配的文件,即文件名为hello.py的文件 ack-grep -g hello.py$ # 查找正则匹配文件,即 *hello.py 的所有文件 ack-grep -g hello --sort-files #查找然后排序
File Inclusion/Exclusion
文件过滤,个人觉得这是一个很不错的功能。如果你曾经在搜索项目源码是不小心命中日志中的某个关键字的话,你会觉得这个有用。
ack-grep --python hello # 查找所有含有字串hello的python文件
ack-grep -G hello.py$ hello # 查找匹配正则的文件, -G已经被 ack2 废弃。
ack配置
也是一个相当赞的功能,对grep了解不多,不知道grep有没有同样的东西。通过配置可以把你的个人习惯做为默认配置,比如我是Python程序员,那默认我要搜索的文件大多数必然是.py的文件。每次搜索时都要输入:--python那就太无聊了。
另外还可以自己指定类型,通过--type-set=conf=.conf,指定一个.conf的文件形式,ack默认提供常见源码的支持。你可以通过:ack-grep --help types查看它支持多少中类型的源码,绝对有你不知道的语言。
下面是一个简单的配置
# 设置排序 --sort-files #设置文件过滤 --python --html --js --conf # 设置显示 --noheading # 定义新的文件类型 --type-set=conf=.conf # 智能识别大小写 --smart-case # 设置以less形式展示,设定less参数 --pager=less -R -M --shift 5 -i
一些资源
- 官网文档
- ack作者采访
- ack-grep和grep对比
- 它还有对应的vim插件,使用方法和在终端上一样。 网址: https://github.com/mileszs/ack.vim
那么都有哪些优化那,看下其特点,有些特性正是grep缺少的(这是我自己总结的):
- 为代码搜索而定制 搜索时默认会忽略”.svn”,”.git”,”CVS”目录,备份文件,二进制文件,core文件等,真是为源码而生.如果觉得还不够,可以在配置文件中设置.
- 默认为递归搜索 源码搜索一般确实都是递归的!
- 可以指定搜索的源码类型 可以通过参数
--xxx
来指定源码类型,比如--python
,--perl
等 - 参数基本与grep一样 大部分参数与grep保持一致
- 存在vim插件(ack.vim),可以方便的通过vim来调用
- ack比grep少一个一个字符.而且排序上ack要比grep靠前多了.
总之,grep
为了搜索而生,ack
为搜索源码而生.
ack常用参数
- -n, 不递归搜索子目录
- -l/L, 显示匹配/不匹配的文件名
- -c, 统计次数
- -v, invert match
- -w, 词匹配
- -i, 忽略大小写
- -f, 只显示文件名,不进行搜索.
安装配置
安装
ubuntu:
apt-get install ack-grep -y
配置
ack可以通过.ackrc
来进行定制.比如下面举例:
# 设置排序
--sort-files
#设置文件过滤
--python
--html
--js
--conf # 设置显示 --noheading # 定义新的文件类型 --type-set=conf=.conf # 智能识别大小写 --smart-case # 设置以less形式展示,设定less参数 --pager=less -R -M --shift 5 -i
Warn: 以上并不是一个推荐的配置,只是举例.个人基本没有进行特别定制
vim与ack结合
在vim中进行多文件查找可以使用自带的vimgrep
命令,但是感觉有点别扭.可以集成ack到vim中,安装插件即可.
搜索ack.vim
插件进行安装,并设置快捷键绑定.这里只是列出我的快捷键绑定.插件的具体安装请参照官方文档,推荐使用vundle来进行插件管理.
nmap <leader>ack :Ack nmap ackw :call Search_Word() function Search_Word() let w = expand("") execute "Ack " . w endfunction
感慨
使用搜索工具有个矛盾点:
- 使用者希望简洁 使用者不确定或者为了方便才进行搜索,渴望简洁.就像google桌面和360的桌面当想搜索的时候按两下
ctrl
就调用出输入口进行. - 程序希望提供精细化的条件指定 如果没有精细化的条件指定,电脑负担就加重了.最要命的是io负载,多人使用的服务器,如果有人随意使用find,grep的话很可能导致其他人卡顿.一旦提供精确一点的条件,命令行输入会非常麻烦.
例如在服务器上,我一般使用grep
基本都会使用-F
和--include
,这样效率会高,但是写起来麻烦.所以看到ack后会很高兴的去试用,并转换.
ack的设计目的上就明确了简化grep,为了更好的搜索代码.所以默认就会排除非代码目录文件.而且还提供了配置文件定制的方式.这很赞!
ack-grep [options] PATTERN [FILE...]
参数说明:
-a : 搜索所有文件(不管什么类型),但是要注意,某些文件是永远都不会搜索的(除非在命令行中指定),比如备份文件。也就是说 -a 并非真的搜索所有文件
-i : 忽略大小写
-v : 输出不匹配的行
-h : 不输出文件名
-H : 强制输出文件名。(默认选项,但搜索单文件时,该选项不是默认的;除此之外,搜索但文件时,无法显示行号,加上这个选项就能显示行号。)
-l : 只输出匹配的文件名
-L: 只输出没有匹配的文件
-f: 仅列出来会搜索哪些文件,不做真实的搜索
-G REGEXP:仅搜索匹配给定正则的文件
-g REGEXP: -f -G REGEXP的缩写
-c, --count: 覆盖正常输出,打印每个文件匹配文本的次数,没有匹配的文件输出0,可使用-l输出匹配的文件。
-1: 只要搜索到第一次匹配就停止搜索
默认查找子目录
示例:
ack-grep --line=1 # 输出所有文件第二行 ack-grep -l 'hello' # 包含的文件名 ack-grep -L 'print' # 非包含文件名 ack-grep -f hello.py # 查找全匹配文件 ack-grep -g hello.py$ # 查找正则匹配文件 ack-grep -g hello --sort-files #查找然后排序 ack-grep --python hello #查找所有python文件 ack-grep -G hello.py$ hello # 查找匹配正则的文件 ack "\bg_.*?\b" # 查找所有以 g_ 开头的单词(word), \b 是单词边界的意思,?跟在*后面表示非贪焚模式。见如下表的lisp正则式规则。 ack "\w*g_.*?\w*" # 查找含有 g_ 的单词(word) grep -R -P -n "[^\x00-\x7F]" # 查找所有非ascii字符,查找中文尤其有用
另外可以修改~/.ackrc来指定ack的配置
lisp正则式规则
字符 |
描述 |
\ |
将下一个字符标记为一个特殊字符、或一个原义字符、或一个 向后引用、或一个八进制转义符。例如,'n' 匹配字符 "n"。'\n' 匹配一个换行符。序列 '\\' 匹配 "\" 而 "\(" 则匹配 "("。 |
^ |
匹配输入字符串的开始位置。如果设置了 RegExp 对象的 Multiline 属性,^ 也匹配 '\n' 或 '\r' 之后的位置。 |
$ |
匹配输入字符串的结束位置。如果设置了 RegExp 对象的 Multiline 属性,$ 也匹配 '\n' 或 '\r' 之前的位置。 |
* |
匹配前面的子表达式零次或多次。例如,zo* 能匹配 "z" 以及 "zoo"。* 等价于{0,}。 |
+ |
匹配前面的子表达式一次或多次。例如,'zo+' 能匹配 "zo" 以及 "zoo",但不能匹配 "z"。+ 等价于 {1,}。 |
? |
匹配前面的子表达式零次或一次。例如,"do(es)?" 可以匹配 "do" 或 "does" 中的"do" 。? 等价于 {0,1}。 |
{n} |
n 是一个非负整数。匹配确定的 n 次。例如,'o{2}' 不能匹配 "Bob" 中的 'o',但是能匹配 "food" 中的两个 o。 |
{n,} |
n 是一个非负整数。至少匹配n 次。例如,'o{2,}' 不能匹配 "Bob" 中的 'o',但能匹配 "foooood" 中的所有 o。'o{1,}' 等价于 'o+'。'o{0,}' 则等价于 'o*'。 |
{n,m} |
m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。例如,"o{1,3}" 将匹配 "fooooood" 中的前三个 o。'o{0,1}' 等价于 'o?'。请注意在逗号和两个数之间不能有空格。 |
? |
当该字符紧跟在任何一个其他限制符 (*, +, ?, {n}, {n,}, {n,m}) 后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串 "oooo",'o+?' 将匹配单个 "o",而 'o+' 将匹配所有 'o'。 |
. |
匹配除 "\n" 之外的任何单个字符。要匹配包括 '\n' 在内的任何字符,请使用象 '[.\n]' 的模式。 |
(pattern) |
匹配 pattern 并获取这一匹配。所获取的匹配可以从产生的 Matches 集合得到,在VBScript 中使用 SubMatches 集合,在 JScript 中则使用 $0…$9 属性。要匹配圆括号字符,请使用 '\(' 或 '\)'。 |
(?:pattern) |
匹配 pattern 但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。这在使用 "或" 字符 (|) 来组合一个模式的各个部分是很有用。例如, 'industr(?:y|ies) 就是一个比 'industry|industries' 更简略的表达式。 |
(?=pattern) |
正向预查,在任何匹配 pattern 的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如, 'Windows (?=95|98|NT|2000)' 能匹配 "Windows 2000" 中的 "Windows" ,但不能匹配 "Windows 3.1" 中的 "Windows"。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。 |
(?!pattern) |
负向预查,在任何不匹配 pattern 的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如'Windows (?!95|98|NT|2000)' 能匹配 "Windows 3.1" 中的 "Windows",但不能匹配 "Windows 2000" 中的 "Windows"。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始 |
x|y |
匹配 x 或 y。例如,'z|food' 能匹配 "z" 或 "food"。'(z|f)ood' 则匹配 "zood" 或 "food"。 |
[xyz] |
字符集合。匹配所包含的任意一个字符。例如,'[abc]' 可以匹配 "plain" 中的 'a'。 |
[^xyz] |
负值字符集合。匹配未包含的任意字符。例如,'[^abc]' 可以匹配 "plain" 中的'p'。 |
[a-z] |
字符范围。匹配指定范围内的任意字符。例如,'[a-z]' 可以匹配 'a' 到 'z' 范围内的任意小写字母字符。 |
[^a-z] |
负值字符范围。匹配任何不在指定范围内的任意字符。例如,'[^a-z]' 可以匹配任何不在 'a' 到 'z' 范围内的任意字符。 |
\b |
匹配一个单词边界,也就是指单词和空格间的位置。例如, 'er\b' 可以匹配 "never" 中的 'er',但不能匹配 "verb" 中的 'er'。 |
\B |
匹配非单词边界。'er\B' 能匹配 "verb" 中的 'er',但不能匹配 "never" 中的 'er'。 |
\cx |
匹配由 x 指明的控制字符。例如, \cM 匹配一个 Control-M 或回车符。x 的值必须为 A-Z 或 a-z 之一。否则,将 c 视为一个原义的 'c' 字符。 |
\d |
匹配一个数字字符。等价于 [0-9]。 |
\D |
匹配一个非数字字符。等价于 [^0-9]。 |
\f |
匹配一个换页符。等价于 \x0c 和 \cL。 |
\n |
匹配一个换行符。等价于 \x0a 和 \cJ。 |
\r |
匹配一个回车符。等价于 \x0d 和 \cM。 |
\s |
匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。 |
\S |
匹配任何非空白字符。等价于 [^ \f\n\r\t\v]。 |
\t |
匹配一个制表符。等价于 \x09 和 \cI。 |
\v |
匹配一个垂直制表符。等价于 \x0b 和 \cK。 |
\w |
匹配包括下划线的任何单词字符。等价于 '[A-Za-z0-9_]'。 |
\W |
匹配任何非单词字符。等价于 '[^A-Za-z0-9_]'。 |
\xn |
匹配 n,其中 n 为十六进制转义值。十六进制转义值必须为确定的两个数字长。例如,'\x41' 匹配 "A"。'\x041' 则等价于 '\x04' & "1"。正则表达式中可以使用 ASCII 编码。. |
\num |
匹配 num,其中 num 是一个正整数。对所获取的匹配的引用。例如,'(.)\1' 匹配两个连续的相同字符。 |
\n |
标识一个八进制转义值或一个向后引用。如果 \n 之前至少 n 个获取的子表达式,则 n 为向后引用。否则,如果 n 为八进制数字 (0-7),则 n 为一个八进制转义值。 |
\nm |
标识一个八进制转义值或一个向后引用。如果 \nm 之前至少有 nm 个获得子表达式,则 nm 为向后引用。如果 \nm 之前至少有 n 个获取,则 n 为一个后跟文字 m 的向后引用。如果前面的条件都不满足,若 n 和 m 均为八进制数字 (0-7),则 \nm 将匹配八进制转义值 nm。 |
\nml |
如果 n 为八进制数字 (0-3),且 m 和 l 均为八进制数字 (0-7),则匹配八进制转义值 nml。 |
\un |
匹配 n,其中 n 是一个用四个十六进制数字表示的 Unicode 字符。例如, \u00A9 匹配版权符号 (©)。 |