Python 子进程 Grep

15 投票
3 回答
45966 浏览
提问于 2025-04-17 05:21

我正在尝试在一个Python脚本中使用grep命令,使用的是subprocess模块。

这是我目前的代码:

userid = 'foo12'
p = subprocess.Popen(['grep', "%s *.log"%userid], stdout=subprocess.PIPE)

但是它什么都没有返回。我不太确定我哪里做错了,所以能不能有人解释一下?我现在使用的方法是加上shell=true,这样可以输出正确的结果,但帮助页面上说这样不安全。我需要帮助,让我的脚本在安全的情况下正常工作。

3 个回答

6

我猜你是想在所有以 '.log' 结尾的文件中查找 'foo12',如果你想用 subprocess 来实现这个功能,你需要把你的代码改成下面这样:

userid = 'foo12'
p = subprocess.Popen('grep %s *.log' % userid, stdout=subprocess.PIPE, shell=True)

shell=True 是必须的,这样才能让通配符生效。当你设置了这个选项后,你需要提供一个字符串命令,而不是一个列表。

另外,确保在提供参数列表时,每个参数都是列表中的一个单独项,你最开始的代码其实相当于下面这样:

grep 'foo12 *.log'
11

这里有两段经过测试的代码可以参考:

>>> print subprocess.check_output(['grep', 'python', 'api_talk.txt'])
Discuss python API patterns
Limitations of python
Introspection in python

>>> print subprocess.check_output('grep python *.txt', shell=True)

如果你希望命令行自动处理通配符,可以使用后者。当shell设置为True时,记得把整个命令放在一个字符串里,而不是分成多个部分。

13

我觉得你遇到了两个问题:

  1. 首先,这段代码:

    p = subprocess.Popen(['grep', "%s *.log"%userid]...
    

    如果不加 shell=True,它不会按预期工作,因为你传给 os.execvp 的参数列表是直接传递的,而这个函数要求每个参数都是一个单独的字符串。你把两个不同的参数合并成了一个字符串(换句话说,grep把"foo12 *.log"当成了要搜索的模式,而不是模式加文件列表)。

    你可以通过这样做来解决这个问题:

    p = subprocess.Popen(['grep', userid, '*.log']...)
    
  2. 第二个问题是,同样如果不加 shell=Trueexecvp并不知道你说的 *.log 是什么意思,它会直接把这个传给grep,而不会经过shell的通配符扩展机制。如果你不想使用 shell=True,你可以尝试这样做:

    import glob
    args = ['grep', userid]
    args.extend(glob.glob('*.log')
    p = subprocess.Popen(args, ...)
    

撰写回答