如何正确读取大型过程输出

时间:2014-06-19 14:21:27

标签: common-lisp sbcl

关于如何从流程流中读取已有few answers,但据我所知,它们不包括从以下流程中读取:

  • 可能会在进行任何输出之前运行很长时间
  • 在其生命周期结束时产生巨大的输出(超过流缓冲区可以容纳)
  • 的运行时间不应超过给定的时间范围
  • 结果输出需要作为一个整体(长字符串)

因此,使用上述解决方案会导致浪费的进程周期,因为即使没有输出,循环也会尝试读取流,或者由于进程无法打印整个输出而导致解决方案无法终止(由于完整缓冲区)输出处理程序在读取之前等待进程终止。

我目前的解决方案看起来像这样(受批量阅读大文件的解决方案的启发)

(defun control-process (process timeout)
  (sb-ext:with-timeout timeout
    (handler-case
      (do ((output-stream (sb-ext:process-output process))
           (string nil))
          ((and (equalp (sb-ext:process-status process) :exited)
            (equalp (peek-char nil output-stream nil :eof) :eof))
           (values string :exited))
        (cond 
          ((equalp (sb-ext:process-status process) :signaled)
           (error 'unexpected-process-finish :finish-status :signaled))
          ((equalp (sb-ext:process-status process) :stopped)
           (error 'unexpected-process-finish :finish-status :stopped)))
        (let ((seq (make-string (file-length output-stream))))
          (read-sequence seq output-stream)
          (setf string (concatenate 'string string seq))
          (sleep 1)))
     (sb-ext:timeout (err)
      (declare (ignore err))
      (values nil :timeout))
     (unexpected-process-finish (err)
      (values nil (finish-status err))))))

使用以下过程调用该函数:

(sb-ext:run-program "/path/to/programm"
                           (list "--params" "foo" "bar") 
                           :output :stream :wait nil)

但这种解决方案有其缺点:

  • 它不起作用,因为流未与文件关联(错误)
  • 即使当时可能没有输出,它也会执行1的通用睡眠
  • 它做了很多串联,这似乎是一个不优雅的解决方案

调用函数处理退出/停止/运行时间过长的最终处理/清理。

我如何阅读以下流程:

  • 可能会运行很长时间(并在其生命周期结束时输出)
  • 可能会使输出大于流缓冲区
  • 的运行时间不得超过给定的时间跨度
  • 整体需要输出

1 个答案:

答案 0 :(得分:5)

字符串输出流可能适合您吗?输出将存储在您之后获得的字符串中,因此缓冲不应该是一个太大的问题。例如,

* (with-output-to-string (out)
    (sb-ext:run-program "/bin/ls" '("/") :output out))

"bin
boot
…
vmlinuz
vmlinuz.old
"

如果您想预先分配字符串,可以使用with-output-to-string string-form 参数来执行该操作。

但是,您不必使用with-output-to-string来使用字符串输出流。您也可以使用make-string-output-stream创建一个并将其传递给sb-ext:run-program。您最终会使用get-output-stream-string获取文本。