【问题标题】:Python - [Subprocess Popen Communicate] hangs on executionPython - [Subprocess Popen Communicate] 挂起执行
【发布时间】:2021-11-16 14:35:34
【问题描述】:

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

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

我在macOS 并使用Python 3.9.7 并尝试在PyCharm IDE 内部执行此操作。

这里是sn-p的代码:

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 subprocess.Popen hanging

Python Script execute commands in Terminal

Python hangs when executing a shell script that runs a process as a daemon

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)

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

【问题讨论】:

  • “PF”是什么意思?
  • @mkrieger1 代表端口转发。我会改变它:)
  • yield 属于哪里?你不能只在函数之外写yield
  • @mkrieger1 没错,它在函数内部。我再次更新了这个问题。 :)
  • 不清楚超时应该在这里实现什么。进程是否输出您需要的信息? Popen 已经知道它创建的进程的 pid

标签: python subprocess


【解决方案1】:

这里的主要问题是communicate。你只想Popen这个进程,然后让它一直运行直到你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

【讨论】:

  • 我不熟悉kubectl,所以我可能误解了您的用例。也许edit你的问题在这种情况下澄清它。
  • 感谢您的回答,非常感谢。 :) 我现在至少对正在发生的事情和原因有了更多的了解。将其改为上下文管理器是什么意思?不幸的是,我不熟悉这些术语。
  • 上下文管理器是您可以在with 语句中使用的东西。易于谷歌:docs.python.org/3/library/contextlib.html
  • @tripleee,仅供参考,如果您在答案下方的评论中使用[edit],它将创建一个链接来编辑答案,而不是问题。
  • 噢,我已经习惯用它来回答问题了……感谢您的反馈。
猜你喜欢
  • 1970-01-01
  • 2015-01-10
  • 2013-01-04
  • 1970-01-01
  • 2018-11-11
  • 2012-10-31
  • 2017-06-10
  • 2018-07-05
  • 2015-08-18
相关资源
最近更新 更多