将pipe命令和sudo与Python子进程结合时出现问题

2024-04-20 09:30:06 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图利用Python模块subprocess在Mac上自动执行终端命令。具体来说,我正在运行一个certain command在我的机器上创建port mappings。但是,该命令需要root权限和管道:

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

为了使用subprocess将根密码传递给shell命令,我按照找到的代码示例here创建以下脚本:

^{pr2}$

注意,为了实现echo输出到命令pfctl -ef -的管道,我创建了两个Popen对象,并按照^{} docs中的建议,将第一个对象的stdout传递给第二个对象的stdin参数,并使用Popen.communicate将根密码写入stdin。在

但是,我上面的脚本不起作用,因为终端仍然提示我输入根密码。奇怪的是,当使用不带管道的命令时,例如,当运行sudo pfctl -s nat(显示我当前的端口映射设置)时,我能够成功地将根密码写入stdin:

p = Popen(['sudo', '-S']+'pfctl -s nat'.split(), stdin=PIPE, stderr=PIPE, universal_newlines=True)
print(p.communicate('root_password\n')[1])

上面的代码可以工作,因为映射配置显示时没有任何密码提示。在

在已经使用Popen.communicate将密码写入stdin之后,如何更改我的第一个Python脚本,从而不提示我输入根密码?在


我在macOS Sierra 10.12.5上运行此代码


Tags: 对象代码命令脚本终端密码管道port
1条回答
网友
1楼 · 发布于 2024-04-20 09:30:06

我想这只是一个简单的管道连接不好的例子。您没有为第一个进程的stdout指定一个管道,因此从外观来看,输出只是打印到终端,然后进程就结束了。在

当第二个进程开始时,它将提示输入密码,据我所知,它将正确接收它。但是,communicate方法随后关闭输入并等待进程完成。据我所知,第一个进程的输出永远不会到达第二个进程,这就是为什么您的脚本不能工作。与其创建一个单独的echo进程,不如用communicate发送所需的所有文本数据?在

另一个问题是sudo直接将提示打印到终端(即通过/dev/tty而不是stdout),而不是{}。在我的sudo(在Debian上)版本中,添加-S选项会导致它将提示打印到stderr。不过,在MAC上,-S选项似乎不能做到这一点。请尝试使用-p ''禁用提示。在

把所有的东西放在一起,这应该是可行的:

from subprocess import PIPE, Popen
from getpass import getpass

password = getpass()

cmd = ['sudo', '-k', '-S', '-p', '', 'pfctl', '-ef', '-']
p = Popen(cmd, stdin=PIPE, stderr=PIPE, universal_newlines=True) 
text = password + '\n'
text += 'rdr pass inet proto tcp from any to any port 80 -> 127.0.0.1 port 8080\n'
p.communicate(text)

安全说明

此答案已更新为不使用纯文本密码。请参阅下面的评论,了解为什么这是一个坏主意的好例子!还请注意,使用Python在内存中存储密码并不完全安全,就像将内存交换到磁盘一样,这将包括明文形式的密码。在较低级别的语言中,mlock系统调用将用于防止任何包含密码的内存被交换。在

相关问题 更多 >