在Python中使用grep -r

4 投票
5 回答
8591 浏览
提问于 2025-04-15 16:46

我想在Python中实现Unix命令'grep -r'的功能。我知道可以用commands.getstatusoutput(),但现在我不想用这个。我想出了这个:

def grep_r (str, dir):
    files = [ o[0]+"/"+f for o in os.walk(dir) for f in o[2] if os.path.isfile(o[0]+"/"+f) ]
    return [ l for f in files for l in open(f) if str in l ]

不过,这个当然没有用到正则表达式,它只是检查'str'是否是'l'的子字符串。所以我尝试了以下这个:

def grep_r (pattern, dir):
    r = re.compile(pattern)
    files = [ o[0]+"/"+f for o in os.walk(dir) for f in o[2] if os.path.isfile(o[0]+"/"+f) ]
    return [ l for f in files for l in open(f) if r.match(l) ]

但是这个不行,它没有给我任何匹配的结果,即使之前的函数能找到匹配。到底是什么改变了?我可以把它拆成一堆嵌套的循环,但我更想要简洁,而不是可读性。

相关问题:

5 个回答

3

把所有这些代码放到一个叫做 pygrep 的文件里,然后运行命令 chmod +x pygrep:

#!/usr/bin/python

import os
import re
import sys

def file_match(fname, pat):
    try:
        f = open(fname, "rt")
    except IOError:
        return
    for i, line in enumerate(f):
        if pat.search(line):
            print "%s: %i: %s" % (fname, i+1, line)
    f.close()


def grep(dir_name, s_pat):
    pat = re.compile(s_pat)
    for dirpath, dirnames, filenames in os.walk(dir_name):
        for fname in filenames:
            fullname = os.path.join(dirpath, fname)
            file_match(fullname, pat)

if len(sys.argv) != 3:
    u = "Usage: pygrep <dir_name> <pattern>\n"
    sys.stderr.write(u)
    sys.exit(1)

grep(sys.argv[1], sys.argv[2])
9

你可能想用 search() 而不是 match(),因为 search() 可以找到行中间的匹配项,具体可以参考这个链接:http://docs.python.org/library/re.html#matching-vs-searching

另外,你的代码结构和意图有点不清晰。我已经把它调整得更符合Python的风格了。

def grep_r (pattern, dir):
    r = re.compile(pattern)
    for parent, dnames, fnames in os.walk(dir):
        for fname in fnames:
            filename = os.path.join(parent, fname)
            if os.path.isfile(filename):
                with open(filename) as f:
                    for line in f:
                        if r.search(line):
                            yield line
6

re.match 只检查字符串的开头。

可以使用 re.search()

来自 官方文档

Python 提供了两种基于正则表达式的基本操作:match 只在字符串的开头检查是否匹配,而 search 则会在字符串的任何位置检查是否匹配(这也是 Perl 默认的行为)。

撰写回答