Python - [Subprocess Popen Communicate] 在执行时挂起

Python - [Subprocess Popen Communicate] hangs on execution

我正在尝试 运行 一个简单的命令,该命令在执行我的自动化测试之前启动端口转发,但它每次都挂起。

最终目标是在会话结束时设置端口转发、获取 PID 并终止端口转发。

我在 macOS 并使用 Python 3.9.7 并尝试在 PyCharm IDE.

中执行此操作

这是代码片段:

def setup_port_forward():

     # command
     command = 'kubectl port-forward api_service 8080:80 -n service_name'

     # shell
     shell_script = subprocess.Popen(command,
                                shell=True,
                                stdin=subprocess.PIPE,
                                stdout=subprocess.PIPE,
                                stderr=subprocess.PIPE,
                                start_new_session=True)

     # extract
     try:
         stdout, stderr = shell_script.communicate(timeout=15)
         pid = int(stdout.decode().strip().split(' ')[-1])
     except subprocess.TimeoutExpired:
         shell_script.kill()

     yield

     # kill session
     os.kill(pid, signal.SIGTERM)

我不会假装知道这是做什么的或它是如何工作的,因为我还在学习 python。

这是我看过的几个主题:

Python Script execute commands in Terminal

Python Script execute commands in Terminal

https://docs.python.org/3/library/subprocess.html#subprocess.Popen.communicate

许多线程说在 shell 脚本中使用 subprocess.PIPE 可能会导致问题,但同样,在关于如何获取 PID 的不同线程上,使用的是这种方法。

我已经尝试使用不同主题中建议的不同方式:

command = 'kubectl port-forward api_service 8080:80 -n service_name'

# 1
os.system(command)

# 2
subprocess.Popen(command).communicate

# 3
subprocess.run(command)

# 4
subprocess.call(command)

# 5
commands.getstatusoutput(command)

他们都挂了。 运行 这是终端,工作正常。

这里的主要问题是 communicate。你只想 Popen 这个过程,然后离开它 运行ning 直到你 kill 它。

你一定会尽可能避免 shell=True;另见 Actual meaning of shell=True in subprocess

我也没有看到 stdoutstderr 重定向有用。如果你只是想安静地 运行 它,可能只是重定向到 subprocess.DEVNULL 和从 subprocess.DEVNULL 重定向。

在这里创建一个单独的会话似乎很可疑;我也许也会放弃它。

Running Bash commands in Python 对在什么情况下首选哪种 subprocess 方法有一些指导。 TLDR 是 subprocess.run 对于您想要等待进程完成的情况,subprocess.Popen 当您不这样做时(但随后了解您在管理对象方面的职责)。

def setup_port_forward():
    proc = subprocess.Popen(
        ['kubectl', 'port-forward', 'api_service', '8080:80', '-n',  'service_name'],
        stdin=subprocess.DEVNULL,
        stdout=subprocess.DEVNULL,
        stderr=subprocess.DEVNULL,
        start_new_session=True)
    yield
    # When done
    proc.kill()

这个函数作为生成器的设计也略显怪异;我可能会建议您改为 context manager