无法使用子进程模块成功解析并运行长命令

时间:2018-10-03 02:17:43

标签: python subprocess

我试图通过在Mac上的port forwarding终端上运行一系列命令来在计算机上实现here。我试图通过使用Python中的subprocess来运行后一个链接中列出的端口映射命令。

我尝试使用subprocess运行的命令是:

echo "
rdr pass inet proto tcp from any to any port 80 -> 127.0.0.1 port 8080
" | sudo pfctl -ef -

上述命令的我的Python实现:

from subprocess import Popen, PIPE
commands = ['echo', '"rdr pass inet proto tcp from any to any port 80 -> 127.0.0.1 port 8080"\n', '|', 'sudo', 'pfctl', '-ef', '-']
p = Popen(['sudo', '-S']+commands, stdin=PIPE, stderr=PIPE, universal_newlines=True)
print(p.communicate('root_password\n')[1])#be able to read password for sudo from stdin

但是,此Python脚本不起作用,因为当我运行sudo pfctl -s nat以在计算机上以预期输出显示所有端口转发规则时

rdr pass inet proto tcp from any to any port = 80 -> 127.0.0.1 port 8080

不显示在外壳上。

但是,运行

echo "
rdr pass inet proto tcp from any to any port 80 -> 127.0.0.1 port 8080
" | sudo pfctl -ef -

确实会产生

rdr pass inet proto tcp from any to any port = 80 -> 127.0.0.1 port 8080
在运行显示命令sudo pfctl -s nat之后

如何改进我的subprocess实现,以便正确输出规则列表?

编辑:奇怪的是,我可以使用sudo pfctl -s nat

运行显示命令subprocess
p = Popen(['sudo', '-S']+'pfctl -s nat'.split(), stdin=PIPE, stderr=PIPE, universal_newlines=True)
print(p.communicate(f'myrootpassword\n')[1]))

在外壳本身中运行时,获得与sudo pfctl -s nat相同的输出。


我正在macOS Sierra 10.12.5上运行命令和Python尝试。

2 个答案:

答案 0 :(得分:2)

您正在尝试使用Popen()来执行包含多个命令的管道,但这种方式不起作用。这是外壳功能。

我相信您需要多次调用Popen(),每个命令一次,并将它们的stdin / stdout明确挂钩。

答案 1 :(得分:1)

subprocess.Popen显式转义外壳元字符(包括|)。例如:

>>> p = Popen(['echo', 'abcdefg', '|', 'wc'], stdout=PIPE, universal_newlines=True)
>>> p.communicate()
('abcdefg | wc\n', None)

请注意,abcdefg没有通过管道传递到wc,因为管道字符被转义并解释为字符而不是过程管道。因此,您的命令不会通过管道传递到sudo中,因为管道字符已转义。您可以使用Popen(将字符串作为命令,而不是列表)来强制 shell=True解释shell元字符:

>>> p = Popen(" ".join(['echo', 'abcdefg', '|', 'wc']), stdout=PIPE, universal_newlines=True, shell=True)
>>> p.communicate()
('       1       1       8\n', None)

如果您不信任您的输入,Shell元字符将被转义,这是有充分的理由的(以防止注入攻击)。如果您这样做了,我认为您是这样做的,因为您要传入根pw,则只需使用shell=True

HTH。