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

Sim*_*Sim 3 sbcl common-lisp

关于如何从流程流中读取已经有一些 答案,但据我所知,它们不包括从以下流程中读取:

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

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

我目前的解决方案看起来像这样(灵感来自批量读取大文件的解决方案)

(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))))))
Run Code Online (Sandbox Code Playgroud)

使用进程调用该函数:

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

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

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

最终处理/清理退出/停止/过长的运行过程由调用函数处理.

我如何阅读以下流程:

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

Jos*_*lor 6

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

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

"bin
boot
…
vmlinuz
vmlinuz.old
"
Run Code Online (Sandbox Code Playgroud)

如果要预先分配字符串,可以使用with-output-to-stringstring-form参数.

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