41.9.4 接收进程输出

异步子进程的输出通常只在 Emacs 等待某类外部事件(如时间流逝或终端输入)时到达。有时在 Lisp 程序中,需要在特定位置显式允许输出到达,甚至等待某个进程的输出到来。

Function: accept-process-output &optional process seconds millisec just-this-one

该函数允许 Emacs 读取进程的待处理输出,并将输出传递给对应的过滤器函数。如果 processnil,函数会一直阻塞,直到从该进程接收到部分输出或进程关闭连接。

参数 secondsmillisec 用于指定超时时间:前者以秒为单位,后者以毫秒为单位,两者相加为总超时时间。即使没有子进程输出,accept-process-output 也会在超时后返回。

参数 millisec 已废弃(不应使用),因为 seconds 支持浮点数,可指定小数秒级等待。若 seconds 为 0,函数仅接收所有待处理输出而不等待。

如果 process 是一个进程,且参数 just-this-onenil,则只处理该进程的输出,暂停其他进程的输出处理,直到从该进程收到输出或超时。若 just-this-one 为整数,还会禁止定时器运行。该功能通常不推荐使用,但在语音合成等特定应用中可能必需。

如果函数从 process(或任意进程,若 processnil)获取到输出,则返回非 nil;即使进程已退出,只要对应连接仍有缓冲数据,也可能返回非 nil。若超时或连接在输出到达前关闭,则返回 nil

如果进程的连接中存在缓冲数据,即使进程已退出,accept-process-output 仍可能返回非 nil。因此,下面这种循环:

;; 该循环存在 bug。
(while (process-live-p process)
  (accept-process-output process))

虽然通常能读取 process 的全部输出,但存在竞态条件:如果连接仍有数据而 process-live-p 已返回 nil,就可能丢失部分输出。更稳妥的写法如下:

(while (accept-process-output process))

如果你在调用 make-process 时传入了非 nilstderr,则会创建一个标准错误进程。See 创建异步进程。这种情况下,等待主进程的输出并不会同时等待标准错误进程的输出。要确保接收完进程的全部标准输出与标准错误,可使用如下代码:

(while (accept-process-output process))
(while (accept-process-output stderr-process))

如果你向 make-processstderr 参数传入了缓冲区,仍需要单独等待标准错误进程,示例如下:

(let* ((stdout (generate-new-buffer "stdout"))
       (stderr (generate-new-buffer "stderr"))
       (process (make-process :name "test"
                              :command '("my-program")
                              :buffer stdout
                              :stderr stderr))
       (stderr-process (get-buffer-process stderr)))
  (unless (and process stderr-process)
    (error "Process unexpectedly nil"))
  (while (accept-process-output process))
  (while (accept-process-output stderr-process)))

只有当两处 accept-process-output 均返回 nil 时,才能确定进程已退出且 Emacs 已读取全部输出。

无法通过这种方式读取远程主机上运行进程的待处理标准错误输出。