終端搜尋工具

菩提樹下的煮茶小童子發表於2017-12-28

做人嘛,開心就好了

為了避免遺忘,先附上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
複製程式碼

做人嘛,開心就好了

相關文章