完全用 Linux 工作 之 临时在文件夹内进行的全文搜索 (fzf)

这里写了一个讲究小而美和工具之间组合的全文搜索的方法。

主要用 fzf,一个效率很高对每一行进行模糊搜索的工具。你可以给它喂一堆任意的字符串,然后从中搜索。

比如用 fd 喂给 fzf 所有目录,然后发送给 cd 来快速跳转目录

# in .bashrc
fcd(){
    cd $(fd --type directory | fzf)
}

如果我想跳转到位置比较深 workbench-schemeracket 目录,只需要输入 很短的 wor rac 然后按回车就够了
image


为了全文搜索,我们需要一个喂给 fzf 所有文件的文件名 + 行号 + 行内容的小脚本 cat-all.py

#!/usr/bin/env python3
import os,sys, subprocess

IGNORE_DIRS=[".git",".vscode","build"]
pwd=os.getcwd()

for  (dirpath, dirnames, filenames) in os.walk(pwd):
    if not any(ig in dirpath for ig in IGNORE_DIRS):
        for file in filenames:
            full_path=dirpath+"/"+file
            relative_path=full_path[len(pwd)+1:]

            mimetype=subprocess.check_output(["file","--dereference","--brief", "--mime-type", full_path]).decode()
            
            if(mimetype.startswith("text")):
                try:
                    with open(full_path,"r") as f:
                        for ln,line in enumerate(f,start=1):
                            if not line.isspace():
                                print(relative_path,"|",ln,"|",line.strip())
                except:
                    print("[FAILED] ",full_path, file=sys.stderr) 

然后就可以这样寻找当前目录下以前写过的所有的 hello, world

cat-all.py | fzf

grep "Hello, World" 区别在于一些 hello_world,“Hello to some World”, “World Hello” 之类的也会被匹配到

如果想按回车直接打开对应的文件,只要把 fzf 输出的内容用 awk 截取第一部分传给 xdg-open 就行了

cat-all.py | fzf | awk '{print $1}' | xargs xdg-open &> /dev/null

据测量在我的电脑上这样搜索 80 万 行 左右的文本需要大约 12 秒左右的时间来索引,且过程中 fzf 占用 ~130mb 的内存。

有问题可以随便问,也欢迎各种指教。

7赞

你的水平很高,是真正的高手,我为你鼓掌

KDE 用户也可以用 baloo

大佬,问个与主题没什么关系的问题,我看到你有在用 LISP,请问你是在用 LISP 做 AutoCAD 开发吗?

@fusionfuture 不是。我只是在编程语言方面兴趣使然,于是就把各种流派,各个时代,比较重要的编程语言都学了一圈 :)

1赞

呃, ripgrep-all 了解一下?

哦不对,你这对应在我这里大概是 ripgrep + search-and-view。热缓存的情况下,120 万行代码,大约五秒吧。大部分时间应该花在我那个 Python 脚本的输出处理上了。

2赞

我看过 ripgrep-all,但我面临的一个问题是需要搜索一些纯文本偏文科的东西。像凭记忆用几个单词来找某个文档的段落的操作用正则来匹配太麻烦了。而且一些专有名词的拼写不确定的情况下的时候 grep 类的工具就直接尬住 (

所以我就全喂给 fzf 来搜了, @lilydjwg

原来如此,所以你选择了交互搜索。模糊的话,有 agrep 可以用。

我大部分时候是搜代码,准确地知道要搜索的字符串的。偶尔也会有不那么清晰的时候,我依旧偏好 ripgrep,因为它快啊。

如果我有一个没格式化的超大单行 JSON :face_with_hand_over_mouth:

你在 cat-all.py 里面条的 if(mimetype.startswith("text") 里判断到 file.endswith(".json") 就用 python 自带的 JSON 模块 dumps(格式化输出) 一下就行了,

除此之外,pdf 文件还可以用 poppler-utils 里面的 pdftotext 来转换成文本照样搜索
.doc 文档也可以用 libreoffice 的 soffice 命令来转换成文本
@color09

赞~~~~~