如何避免使用 Popen 将 shell 构造传递给可执行文件
How to avoid passing shell constructs to executable using 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'、'temp.log'] 传递给 foo 可执行文件。
如何在保持功能的同时摆脱这些传递给 foo 的额外参数?
我已经尝试 shell=True 但阅读了有关出于安全目的避免它的信息(shell 注入攻击)。寻找一个简洁的解决方案。
谢谢
更新:
- 更新了 tee 命令后的文件
您可以合并两个 Whosebug 问题的答案:
1. piping together several subprocesses
x | y
问题
2. Merging a Python script's subprocess' stdout and stderr (while keeping them distinguishable)
2>&1
问题
字符串
./main/foo --config config_file 2>&1 | /usr/bin/tee >temp.log
...充满了 shell 结构。 如果游戏中没有 shell,这些就没有任何意义。因此,您有两个选择:
- 设置
shell=True
- 将它们替换为本机 Python 代码。
例如,2>&1
与将 stderr=subprocess.STDOUT
传递给 Popen
是一样的,而您的 T 恤——因为它的输出被重定向并且没有传递任何参数——可能只是替换为 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
, 而不是 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
和 return foo
的退出状态,而不是 tee
,事情会变得更有趣.考虑以下因素:
p = subprocess.Popen(['bash', '-c', 'set -o pipefail; ' + command_str])
...pipefail
bash 扩展将强制 shell 以第一个管道组件失败的状态退出(如果没有组件失败则为 0),而不是而不是仅使用最终组件的退出状态。
除了 之外,还有几个 "neat" 代码示例。
到运行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?
我正在尝试调用名为 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'、'temp.log'] 传递给 foo 可执行文件。
如何在保持功能的同时摆脱这些传递给 foo 的额外参数? 我已经尝试 shell=True 但阅读了有关出于安全目的避免它的信息(shell 注入攻击)。寻找一个简洁的解决方案。
谢谢
更新: - 更新了 tee 命令后的文件
您可以合并两个 Whosebug 问题的答案:
1. piping together several subprocesses
x | y
问题
2. Merging a Python script's subprocess' stdout and stderr (while keeping them distinguishable)
2>&1
问题
字符串
./main/foo --config config_file 2>&1 | /usr/bin/tee >temp.log
...充满了 shell 结构。 如果游戏中没有 shell,这些就没有任何意义。因此,您有两个选择:
- 设置
shell=True
- 将它们替换为本机 Python 代码。
例如,2>&1
与将 stderr=subprocess.STDOUT
传递给 Popen
是一样的,而您的 T 恤——因为它的输出被重定向并且没有传递任何参数——可能只是替换为 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
, 而不是 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
和 return foo
的退出状态,而不是 tee
,事情会变得更有趣.考虑以下因素:
p = subprocess.Popen(['bash', '-c', 'set -o pipefail; ' + command_str])
...pipefail
bash 扩展将强制 shell 以第一个管道组件失败的状态退出(如果没有组件失败则为 0),而不是而不是仅使用最终组件的退出状态。
除了
到运行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?