如何避免使用Popen将shell结构传递给可执行文件

时间:2015-08-11 21:10:40

标签: python shell popen

我试图调用一个名为foo的可执行文件,并传递一些命令行参数。外部脚本调用可执行文件并使用以下命令:

./main/foo --config config_file 2>&1 | /usr/bin/tee temp.log

该脚本使用Popen执行此命令,如下所示:

from subprocess import Popen
from subprocess import PIPE
def run_command(command, returnObject=False):
    cmd = command.split(' ')
    print('%s' % cmd)
    p = None
    print('command : %s' % command)
    if returnObject:
        p = Popen(cmd)
    else:
        p = Popen(cmd)
        p.communicate()
        print('returncode: %s' % p.returncode)
        return p.returncode
    return p

command = "./main/foo --config config_file 2>&1 | /usr/bin/tee temp.log
"
run_command(command)

然而,这传递了额外的论据[' 2>& 1',' |',' / usr / bin / tee',&#39 ; temp.log']到foo可执行文件。

如何在保持功能的同时摆脱传递给foo的额外参数? 我已经尝试过shell = True但是为了安全起见(shell注入攻击),请阅读有关避免它的内容。寻找一个简洁的解决方案。

由于

更新: - 按照tee命令

更新了文件

3 个答案:

答案 0 :(得分:3)

字符串

./main/foo --config config_file 2>&1 | /usr/bin/tee >temp.log

...充满了shell结构。 如果没有正在使用的shell,这些对任何事都没有意义。因此,您有两种选择:

  • 设置shell=True
  • 用原生Python代码替换它们。

例如,2>&1与将stderr=subprocess.STDOUT传递给Popen是一回事,而你的三通 - 因为它的输出被重定向并且没有传递任何参数 - 只能被替换与stdout=open('temp.log', 'w')

因此:

p = subprocess.Popen(['./main/foo', '--config', 'config_file'],
  stderr=subprocess.STDOUT,
  stdout=open('temp.log', 'w'))

...或者,如果你真的 想要tee命令,但只是错误地使用它(也就是说,如果你想要tee temp.log,而不是{{ 1}}):

tee >temp.log

在函数中包含它,并检查两端的成功可能如下所示:

p1 = subprocess.Popen(['./main/foo', '--config', 'config_file'],
  stderr=subprocess.STDOUT,
  stdout=subprocess.PIPE)
p2 = subprocess.Popen(['tee', 'temp.log'], stdin=p1.stdout)
p1.stdout.close() # drop our own handle so p2's stdin is the only handle on p1.stdout
stdout, _ = p2.communicate()

顺便说一下 - 如果你想使用def run(): p1 = subprocess.Popen(['./main/foo', '--config', 'config_file'], stderr=subprocess.STDOUT, stdout=subprocess.PIPE) p2 = subprocess.Popen(['tee', 'temp.log'], stdin=p1.stdout) p1.stdout.close() # drop our own handle so p2's stdin is the only handle on p1.stdout # True if both processes were successful, False otherwise return (p2.wait() == 0 && p1.wait() == 0) 并返回shell=True的退出状态,而不是foo,事情会变得更有趣。请考虑以下事项:

tee

... p = subprocess.Popen(['bash', '-c', 'set -o pipefail; ' + command_str]) bash扩展将强制shell退出,第一个管道组件的状态失败(如果没有组件失败,则为0),而不是仅使用最终组件的退出状态

答案 1 :(得分:1)

除了the explanation from @Charles Duffy answer之外,还有一些“简洁”的代码示例。

在Python中运行shell命令:

#!/usr/bin/env python
from subprocess import check_call

check_call("./main/foo --config config_file 2>&1 | /usr/bin/tee temp.log",
           shell=True)

没有shell:

#!/usr/bin/env python
from subprocess import Popen, PIPE, STDOUT

tee = Popen(["/usr/bin/tee", "temp.log"], stdin=PIPE)
foo = Popen("./main/foo --config config_file".split(), 
            stdout=tee.stdin, stderr=STDOUT)
pipestatus = [foo.wait(), tee.wait()]

注意:请勿将"command arg".split()与非文字字符串一起使用。

请参阅How do I use subprocess.Popen to connect multiple processes by pipes?

答案 2 :(得分:0)

您可以将两个StackOverflow问题的答案组合在一起:
1. piping together several subprocesses
   x | y问题 2. Merging a Python script's subprocess' stdout and stderr (while keeping them distinguishable)
2>&1问题