Python-将dnsmasq命令与subprocess.Popen()一起使用

Python-将dnsmasq命令与subprocess.Popen()一起使用,python,linux,Python,Linux,我需要自动执行一系列应该输入linux终端的命令,以便为激光雷达传感器配置dhcp服务器。命令如下所示: $ sudo -S ip addr flush dev enp3s0f1 $ ip addr show dev enp3s0f1 $ sudo -S ip addr add 10.5.5.1/24 dev enp3s0f1 $ sudo ip link set enp3s0f1 up $ ip addr show dev enp3s0f1 $ sudo dnsmasq -C /dev/nul

我需要自动执行一系列应该输入linux终端的命令,以便为激光雷达传感器配置dhcp服务器。命令如下所示:

$ sudo -S ip addr flush dev enp3s0f1
$ ip addr show dev enp3s0f1
$ sudo -S ip addr add 10.5.5.1/24 dev enp3s0f1
$ sudo ip link set enp3s0f1 up
$ ip addr show dev enp3s0f1
$ sudo dnsmasq -C /dev/null -kd -F 10.5.5.50,10.5.5.100 -i enp3s0f1 --bind-dynamic
为此,我使用了
subprocess.Popen()
类。我创建了一个名为Terminal的类,用来处理以字符串形式显示的命令:

from shlex import split
from subprocess import Popen, PIPE, STDOUT, call

class Terminal:
    def __init__(self):
        self.sudoPassword = "mySudoPassword"
        self.ethernetPort = "enp3s0f1"
        self.ipAddress = "10.5.5.1/24"
        self.ipRange = "10.5.5.95,10.5.5.95"
    
    # series of commands for connecting with the sensor
    def connect_sensor(self):
        if self.ping("10.5.5.95"):
            print("Already connected")
        else:
            print(self.command("sudo -S ip addr flush dev enp3s0f1"))
            print(self.command("ip addr show dev enp3s0f1"))
            print(self.command("sudo -S ip addr add 10.5.5.1/24 dev enp3s0f1"))
            print(self.command("sudo ip link set enp3s0f1 up"))
            print(self.command("ip addr show dev enp3s0f1"))
            print(self.command("sudo dnsmasq -C /dev/null -kd -F 10.5.5.50,10.5.5.100 -i enp3s0f1 --bind-dynamic"))
    
    # excecute a command as a string
    def command(self, string):
        print("excecuting {}".format(string))
        args = split(string)
        if args[0] == "sudo":
            with Popen(args, stdin=PIPE, stdout=PIPE) as proc:
                outs, errs = proc.communicate('{}\n'.format(self.sudoPassword).encode("utf-8"))
                print("\n")
                return outs.decode('utf-8')
        else:
            with Popen(args, stdout=PIPE) as proc:
                print("\n")
                return proc.stdout.read().decode('utf-8')
    
    # check if the sensor is connected
    def ping(self, host):
        param = '-c'
        command = ['ping', param, '1', host]
        return call(command) == 0
在我尝试使用
print(self.command(“sudo dnsmasq-C/dev/null-kd-F 10.5.5.50,10.5.5.100-I enp3s0f1--bind dynamic”)运行最后一个命令之前,一切都运行得很好。
,这应该会产生以下输出:

dnsmasq: started, version 2.80 cachesize 150
dnsmasq: compile time options: IPv6 GNU-getopt DBus i18n IDN DHCP DHCPv6 no-Lua TFTP conntrack ipset auth DNSSEC loop-detect inotify dumpfile
dnsmasq-dhcp: DHCP, IP range 10.5.5.50 -- 10.5.5.100, lease time 1h
dnsmasq-dhcp: DHCP, sockets bound exclusively to interface enp3s0f1
dnsmasq: reading /etc/resolv.conf
dnsmasq: using nameserver 127.0.0.53#53
dnsmasq: read /etc/hosts - 7 addresses
一段时间后,如果传感器已连接,则应将以下线路添加到输出:

dnsmasq-dhcp: DHCPDISCOVER(enp3s0f1) 10.5.5.95 bc:0f:a7:00:1a:91 
dnsmasq-dhcp: DHCPOFFER(enp3s0f1) 10.5.5.95 bc:0f:a7:00:1a:91 
dnsmasq-dhcp: DHCPDISCOVER(enp3s0f1) 10.5.5.95 bc:0f:a7:00:1a:91 
dnsmasq-dhcp: DHCPOFFER(enp3s0f1) 10.5.5.95 bc:0f:a7:00:1a:91 
dnsmasq-dhcp: DHCPREQUEST(enp3s0f1) 10.5.5.95 bc:0f:a7:00:1a:91 
dnsmasq-dhcp: DHCPACK(enp3s0f1) 10.5.5.95 bc:0f:a7:00:1a:91 os-992023000242
我需要将整个输出提取为字符串,以便以编程方式检查传感器是否已成功连接到我的计算机,但是代码在第29行被阻塞

outs, errs = proc.communicate('{}\n'.format(self.sudoPassword).encode("utf-8"))
所需的输出显示为错误

具体来说,我需要解决以下两个问题:

  • 代码不能一直被阻止。(Popen.communicate()是一个阻塞函数吗?)
  • 必须以编程方式连续检查Popen输出,以查看是否返回字符串
    “DHCPACK”
    。发生这种情况时,传感器已连接,因此我可以继续执行下一步
  • 我尝试向Popen.communicate()添加超时,但似乎没有任何效果:

    outs, errs = proc.communicate('{}\n'.format(self.sudoPassword).encode("utf-8"), timeout=1)
    
    我还尝试将
    stderr
    输出定向到
    stdout
    ,在这种情况下,没有任何输出,函数也会阻止代码:

         with Popen(args, stdin=PIPE, stdout=PIPE, stderr=STDOUT) as proc:
            outs, errs = proc.communicate('{}\n'.format(self.sudoPassword).encode("utf-8"), timeout=1)
            print("\n")
            return outs.decode('utf-8')
    
    我使用
    sudo python3 main.py
    从终端运行程序,但没有成功。 我使用的是Ubuntu20.04和Python3.8.2,文档中说“从stdout和stderr读取数据,直到到达文件末尾。”这意味着是的,它是一个阻塞函数。由于
    dnsmasq
    不会退出,
    communicate
    将永远看不到EOF,您对
    communicate
    的调用将永远不会返回。