终端搜索工具

做人嘛,开心就好了

为了避免遗忘,先附上repo链接,有兴趣的拿去用咯。

https://github.com/guoruibiao/worktools/tree/master/searcher

前言

平时都是在终端下进行开发,文件少代码量不大的时候,查找某些方法也好,关键字也罢,都还可以,不算费时。但是随着代码量的不断增加,项目越写越大,很多文件,方法就根本找不到到底在哪个地方了。这个时候再去一个一个的找的话,就不好玩了。

也许你会说,我有IDE,全局搜索下不就好了,干嘛这么费事咧。是的,IDE有其独特的优点。但是完全在终端下工作,就用不了纯粹的IDE了。VIM中有一个插件,叫ctrlp。在normal模式下按下Ctrl+P键,就可以查找本级目录(以及子目录)下包含有输入的关键字的文件了。如下图。
VIM插件Ctrlp的文件查询效果

相信你也能看出来了,ctrlp能找到的只是一个文件名,对于内部的变量还是心有余而力不足的。当然了,在VIM中其实也不是个事。各种插件工具,搞一搞,不输IDE。但是这和今天要写的工具的预期有点差距。我们要找到某个目录下包含某个方法,某个关键字的具体的位置。

在正式开始制作工具之前,下面需要先熟悉一下一些基础的东西。

查找

查找,基本上分为两块。一个是查找文件,一个是查找内容。

文件查找

最常用的文件查找命令是find。

find path -name "regex "
# 示例
➜  worktools git:(master) ✗ find ./ -name "*.p*"
.//searcher/colorcmd.pyc
.//searcher/searcher.py
.//searcher/colorcmd.py
.//sqlhelper/sqlhelper.py
.//sqlhelper/datatransfer.py
.//dingding/dingding.py
.//interfacetool.py
.//getrealip.py
.//detect-actions.py
.//getall/finder.py
.//getall/get.py
.//redis-analyzer/server.py
.//redis-analyzer/redishelper.py
.//redis-analyzer/__init__.py
.//redis-analyzer/__pycache__/redishelper.cpython-36.pyc
.//redis-analyzer/temp.py

可以看出find还会帮我们进行递归式的查找。

内容查找

实现内容查找的方式有很多方式,使用grep命令,或者使用Perl,Python,shell等脚本语言来做处理都是可以的。当然,不同的方式实现的最终效果也会有差距。

如果只是简单的想知道哪个文件包含了目标关键字,使用grep就可以了。

➜  worktools git:(master) ✗ grep 递归 searcher/searcher.py 
        # 明天做下递归版本
➜  worktools git:(master) ✗

但是如果想知道包含了关键字在(多个)文件中的行数,位置,这个时候在使用grep等命令就有点捉襟见肘了。但也不是说不能实现,如:

➜  worktools git:(master) ✗ find ./ -name "*.p*" | xargs grep 递归
.//searcher/searcher.py: # 明天做下递归版本
➜  worktools git:(master) ✗ find ./ -name "*.p*" | xargs grep hello
.//interfacetool.py:#cmd = "wget http://fanyi.badu.com/v2transapi?query=hello | python -m json.tool"
➜  worktools git:(master) ✗ find ./ -name "*.p*" | xargs grep coding
Binary file .//searcher/colorcmd.pyc matches
.//searcher/searcher.py:#coding: utf8
.//searcher/searcher.py:sys.setdefaultencoding("utf8")
.//searcher/colorcmd.py:# coding: utf8
.//searcher/colorcmd.py:sys.setdefaultencoding("utf8")
.//sqlhelper/sqlhelper.py:# coding: utf8
.//sqlhelper/datatransfer.py:# coding: utf8
.//sqlhelper/datatransfer.py:sys.setdefaultencoding('utf8')
.//dingding/dingding.py:# coding: utf8
.//interfacetool.py:# coding: utf8
.//getrealip.py:# coding: utf8
.//detect-actions.py:# coding: utf8
.//getall/finder.py:# coding: utf8
.//getall/get.py:# coding: utf8
.//redis-analyzer/server.py:# coding: utf8
.//redis-analyzer/server.py:sys.setdefaultencoding('utf8')
.//redis-analyzer/redishelper.py:# coding: utf8
.//redis-analyzer/__init__.py:# coding: utf8
.//redis-analyzer/temp.py:# coding: utf8
➜  worktools git:(master) ✗

而使用一些稍微高级一点的脚本语言,能实现的功能就会更多样化。比如高亮显示查找的关键字,添加行号元数据等等,这些使用高级语言,会更方便一点。

高亮工具

大二的时候接触的Python,一开始也是在命令行里面不断摸索这,从理解命令行参数的使用到自己封装了一个getpass2的库。什么进度条啊的都算是玩了下。在这么多的库中,有一个让我确实印象深刻。那就是colorama。一个可以让非黑即白的终端瞬间变得多姿多彩起来。

对我而言,colorama足够好用,但是init(autoreset=True)有时候并不能满足我的需求。比如我只想高亮某个关键字,需要操作的那就太多了。于是我打算自己写一个类似的,满足我的需求就好了,于是有了colorcmd。在正式写代码之前,还是要先理解下如何让终端输出多种颜色。

知识点普及

在支持真彩色的终端中,有这么一个约定。

ESC键的转移序列为ASCII码的\033.
变换颜色的格式如下:

\033[显示方式;前景色;背景色m

需要注意的是:显示方式,前景色,背景色至少存在一个就可以。如果存在多个,记得使用英文的分号进行分割。

显示方式有如下取值:

  • 0 关闭所有效果
  • 1 高亮
  • 4 下划线
  • 5 闪烁
  • 7 反色
  • 8 不可见

显示方式特效

前景色以3开头,背景色以4开头。紧邻的为颜色取值,分别为:
- 0 黑色
- 1 红色
- 2 绿色
- 3 黄色
- 4 蓝色
- 5 紫色
- 6 青色
- 7 白色

简单的来测试下。
字体颜色特征

工具编写

这里我打算使用shell配合Python实现一个关键字高亮搜索的小工具。具体会有如下文件:

colorcmd.py 终端颜色样式工具类
searcher.py 关键字搜索
searcher.sh 文件搜索

searcher.sh

#!/usr/bin bash
# 使用shell配合Python脚本查找文件中某一个变量或者字符串所在的行数


filelist=`find $1 -name "*.*"`

for file in ${filelist[@]};do
    #echo $file;
    python /Users/changba164/guo/tools/worktools-master/worktools/searcher/searcher.py $file $2
done;

#find $1 -name "*.*" | xargs python $2

searcher.py

#!/usr/bin python
#coding: utf8
import sys
reload(sys)
sys.setdefaultencoding("utf8")
import re
import os
from colorcmd import Color, Style, Enhancer


def find(filepath, keyword):
    if os.path.isdir(filepath):
        # 明天做下递归版本
        return []
    result = []
    with open(filepath, 'r') as file:
        lines = file.readlines()
        file.close()
        # 遍历每一行,读取包含关键字的行,并进行临时存储,用于后续美化输出
    counter = 0
    for line in lines:
        counter += 1
        if keyword.lower() in line.lower():
            wrappedword = Enhancer.mix(keyword, Color.BLACK_DEEPGREEN, Style.HIGHLIGHT+Style.UNDERLINE+Style.BLINK)
            tmp = {"number": counter, "line":line.rstrip("\n").replace(keyword, wrappedword)}
            result.append(tmp)
    return result

def pretty_print(filepath, rows):
    for row in rows:
        if row is not None or row != []:
            print "-------"*5 + filepath + "-------"*5
            print "Line: {}\t {}".format(row['number'], row['line'])
filepath = sys.argv[1]
keyword = sys.argv[2]
rows = find(filepath, keyword)
pretty_print(filepath, rows)

colorcmd.py

#!/usr/bin python
# coding: utf8
import sys
reload(sys)
sys.setdefaultencoding("utf8")
""" # 之前用过一个colorama的库,挺好用的,但是有一个缺点就是有时候init(autoreset=True)并不很好使,究其原因,还是设计层面的问题 于是我打算使用“包装”的思想,来做一个更好用一点的出来。 """

class Color(object):
    CLEAR = "\33[0m"
    # 字体颜色 前景色
    FORE_BLACK = "\33[30m"
    FORE_RED = "\33[31m"
    FORE_GREEN = "\33[32m"
    FORE_YELLOW = "\33[33m"
    FORE_BLUE = "\33[34m"
    FORE_PURPLE = "\33[35m"
    FORE_DEEPGREEN = "\33[36m"
    FORE_WHITE = "\33[37m"
    # 背景色
    BACK_BLACK = "\33[40"
    BACK_RED = BACK_DEEPRED = "\33[41m"
    BACK_GREEN = "\33[42m"
    BACK_YELLOW = "\33[43m"
    BACK_BLUE = "\33[44m"
    BACK_PURPLE = "\33[45m"
    BACK_DEEPGREEN = "\33[46m"
    BACK_WHITE = "\33[47m"
    # 黑底彩色
    BLACK_BLACK = "\33[90m"
    BLACK_RED = BLACK_DEEPRED = "\33[91m"
    BLACK_GREEN = "\33[92m"
    BLACK_YELLOW = "\33[93m"
    BLACK_BLUE = "\33[94m"
    BLACK_PURPLE = "\33[95m"
    BLACK_DEEPGREEN = "\33[96m"
    BLACK_WHITE = "\33[97m"
    """ 颜色相关工具类 """
    def __init__(self):
        pass


class Style(object):
    CLEAR = "\33[0m"
    HIGHLIGHT = "\33[1m"
    UNDERLINE = "\33[4m"
    BLINK = "\33[5m"
    REVERSE = "\33[7m"
    BLANKING = "\33[8m"
    """ 样式相关,前景色,背景色,加粗,下划线等 """
    def __init(self):
        pass

class Enhancer(object):
    """ 曾经有一个tag的交叉叠加,给了我这个思路。目标是做成一个无限叠加的增强品。 """
    def __init__(self):
        pass

    @staticmethod
    def highlight(text="", color=Color.FORE_RED, style=Style.CLEAR):
        return Style.HIGHLIGHT + style + color + text + Style.CLEAR

    @staticmethod
    def mix(text, color=Color.CLEAR, style=Style.CLEAR, highlight=False):
        return style + color + text + Style.CLEAR
if __name__ == "__main__":
    #print "\33[5m"+Color.FORE_GREEN+"Hello World!"+"\33[0m"
    text = "郭璞"
    print Enhancer.highlight(text, Color.BACK_GREEN, Style.BLINK)
    print Enhancer.mix("what a amazing colorama!", Color.BLACK_PURPLE, Style.UNDERLINE+Style.HIGHLIGHT+Style.BLINK)

代码比较简单,但是还是有很大的拓展空间的。

  • 比如以多线程的形式进行查找,这样速度会更加迅速。

  • 终端输出的美化效果,现在就是个简单的输出了,如果有必要的话,可以借助PrettyTable这个库实现更优雅的输出效果。

如何使用

这个工具的入口是searcher.sh,所以正常使用的话可以这么干:

sh ./searcher.sh targetpath keyword
# 示例
➜  searcher git:(master) ✗ sh searcher.sh ./ default
---------.//searcher.py------------
Line: 5  sys.setdefaultencoding("utf8")
---------.//colorcmd.py------------
Line: 5  sys.setdefaultencoding("utf8")
➜  searcher git:(master) ✗

但是这样每次都要输入一下sh命令 挺麻烦的。因此,放到alias里面就好多了。

vim ~/.zshrc (我的Mac安装了zsh,所以这里是~/.zshrc, 如果你用的是Linux,那么应该是~/.bashrc. 没有的话就新建一个。)
# 在最后面加上这么一行命令。
alias search='sh /absolute path/searcher.sh'
# 保存退出后还差一句命令,让别名的配置可以在当前的会话终端内生效。
source ~/.zshrc

完成后就可以很方便的在终端内搜索了。
格式如下:

search path keyword

做人嘛,开心就好了

相关文章
相关标签/搜索