首页 > 解决方案 > Python 子流程模块:如何重新打开 PIPE?

问题描述

我有一段代码需要运行 node js。为了写入文件以供节点 js 接收命令,我使用以下代码:

def execute(comm):
    file_number = proc.stdin.fileno()
    file = open(file_number, "w+")
    file.write(comm)
    file.close()

execute("""console.log("hi");""")
execute("""console.log("bye");""")

但是,当我第二次运行执行时,出现以下错误:

OSError: [WinError 6] The handle is invalid

这个错误是什么意思,我该如何解决?这是完整的代码:

import subprocess
import threading
import time

class nodePrompt:
    def __init__(self):
        self.proc = subprocess.Popen("node", shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
        self.on_out_func = print
        self.on_out_args=[]
        self.on_out_kwargs={}
        self.on_err_func = print
        self.on_err_args=[]
        self.on_err_kwargs={}
        self.on_out_thread=None
        self.on_err_thread=None

    def execute(self, comm):
        file_number = self.proc.stdin.fileno()
        file = open(file_number, "w+")
        file.write(comm)
        file.close()


    def on_out(function, *args, **kwargs):
        self.on_out_func=function
        self.on_out_args=args
        self.on_out_kwargs=kwargs

    def on_err(function, *args, **kwargs):
        self.on_err_func=function
        self.on_err_args=args
        self.on_err_kwargs=kwargs

    def on_out_handle(self):
        for i in iter(self.proc.stdout.readline, ""):
            self.on_out_func(i, *self.on_out_args, **self.on_out_kwargs)

    def start_on_out(self, daemon=False):
        self.on_out_thread=threading.Thread(target=self.on_out_handle)
        self.on_out_thread.setDaemon(daemon)
        self.on_out_thread.start()

    def on_err_handle(self):
        for i in iter(self.proc.stderr.readline, ""):
            self.on_err_func(i, *self.on_err_args, **self.on_err_kwargs)

    def start_on_err(self, daemon=False):
        self.on_err_thread=threading.Thread(target=self.on_err_handle)
        self.on_err_thread.setDaemon(daemon)
        self.on_err_thread.start()

prompt = nodePrompt()
prompt.start_on_out()
prompt.start_on_err()
prompt.execute("console.log(\"HI\");")
time.sleep(1)
prompt.execute("console.log(\"HI\");")

标签: pythonsubprocess

解决方案


关闭的 FIFO 无法重新打开。你所能做的就是在你真正完成之前不要关闭它。改为使用flush()以确保立即发生写入。

这是 node.js 在其输入和输出进入管道时没有增量运行代码(或不刷新其输出)的问题。在修复您的代码以self.proc.stdin直接使用之后,Python 可以证明是正确的。

即修改后execute定义为:

    def execute(self, comm):
        self.proc.stdin.write(comm)
        self.proc.stdin.flush()

...我们可以监控 Python 解释器和它启动的 node.js 副本的活动。

不幸的是,node它的行为不像你想要的那样。

请注意以下跟踪的时间戳,它用于sysdig跟踪程序执行期间的操作:

5178 18:55:37.047606506 4 python (18260) < write res=19 data=console.log("HI");. 
8239 18:55:37.095849210 1 node (18261) < read res=19 data=console.log("HI");. 
8590 18:55:38.048679102 4 python (18260) < write res=19 data=console.log("HI");. 
8595 18:55:38.048710436 4 python (18260) < close res=0 
8597 18:55:38.048742124 1 node (18261) < read res=19 data=console.log("HI");. 
8603 18:55:38.048911687 1 node (18261) < read res=0 data= 
8633 18:55:38.051116830 1 node (18261) < write res=3 data=HI. 
8634 18:55:38.051158022 6 python (18262) < read res=3 data=HI. 
8636 18:55:38.051199286 6 python (18262) < write res=3 data=HI. 
8642 18:55:38.051400907 1 node (18261) < write res=3 data=HI. 
8643 18:55:38.051441455 6 python (18262) < read res=3 data=HI. 
8645 18:55:38.051459654 6 python (18262) < write res=3 data=HI. 

Python 编写第一个console.log("HI")Node 读取它,早在 Node 写入任何输出之前。

我建议查看 Node 是否运行来自非 TTY 源的代码,或者它是否尝试将整个流读取到末尾并在运行任何内容之前对其进行解析。

推荐的复制器

如果您要向节点受众提出这个问题,请考虑为他们提供以下 bash 脚本:

{
  echo 'console.log("HI");'
  sleep 1
  echo 'console.log("HI");'
  sleep 1
  echo "Already sent both commands to node" >&2
} | node

它的输出是:

Already sent both commands to node
HI
HI

...而人们可能希望早点打印至少一个 HI,因为在发送第一个命令后有整整两秒的延迟。


推荐阅读