我正在尝试在SBCL中运行外部程序并捕获其输出。 输出是二进制数据(png图像),而SBCL坚持将其解释为字符串。
我尝试了很多方法,比如
(trivial-shell:shell-command "/path/to/png-generator" :input "some input")
(with-input-from-string (input "some input")
(with-output-to-string (output)
(run-program "/path/to/png-generator" () :input input :output output))
(with-input-from-string (input "some input")
(flexi-streams:with-output-to-sequence (output)
(run-program "/path/to/png-generator" () :input input :output output))
但是我得到了像
这样的错误Illegal :UTF-8 character starting at byte position 0.
在我看来,SBCL正试图将二进制数据解释为文本并对其进行解码。我该如何改变这种行为?我只对获取八位字节的向量感兴趣。
编辑:由于上面的文字不清楚,我想补充一点,至少在flexi-stream的情况下,流的元素类型是flexi-streams:octect
(这是一个(unsigned-byte 8)
)。
我希望至少在这种情况下run-program
能够读取原始字节而不会出现很多问题。相反,我收到了Don't know how to copy to stream of element-type (UNSIGNED-BYTE 8)
答案 0 :(得分:4)
编辑:我因无法完成这项非常简单的任务而感到生气并解决了问题。
从功能上讲,将UNSIGNED-BYTE类型的流发送到运行程序并使其正常工作的能力受到严重限制,原因我不明白。我试过像你这样的灰色流,灵活流,fd流和其他一些机制。
然而,仔细阅读run-program的源代码(第五次或第六次),我注意到有一个选项:STREAM你可以传递给输出。鉴于此,我想知道读取字节是否有效......它确实如此。为了获得更高效的工作,可以确定如何获取非文件流的长度并在其上运行READ-SEQUENCE。
(let*
;; Get random bytes
((proc-var (sb-ext:run-program "head" '("-c" "10" "/dev/urandom")
:search t
;; let SBCL figure out the storage type. This is what solved the problem.
:output :stream))
;; Obtain the streams from the process object.
(output (process-output proc-var))
(err (process-error proc-var)))
(values
;;return both stdout and stderr, just for polish.
;; do a byte read and turn it into a vector.
(concatenate 'vector
;; A byte with value 0 is *not* value nil. Yay for Lisp!
(loop for byte = (read-byte output nil)
while byte
collect byte))
;; repeat for stderr
(concatenate 'vector
(loop for byte = (read-byte err nil)
while byte
collect byte))))
答案 1 :(得分:2)
如果您愿意使用某些外部库,可以使用babel-streams完成。这是我用来安全地从程序中获取内容的函数。我使用:latin-1因为它将前256个字节映射到字符。你可以删除八位字节到字符串并有矢量。
如果你也想要stderr,你可以使用嵌套的'with-output-to-sequence'来获得两者。
(defun safe-shell (command &rest args)
(octets-to-string
(with-output-to-sequence (stream :external-format :latin-1)
(let ((proc (sb-ext:run-program command args :search t :wait t :output stream)))
(case (sb-ext:process-status proc)
(:exited (unless (zerop (sb-ext:process-exit-code proc))
(error "Error in command")))
(t (error "Unable to terminate process")))))
:encoding :latin-1))
答案 2 :(得分:2)
Paul Nathan已经就如何从程序中读取I / O作为二进制文件给出了一个非常完整的答案,所以我只需添加为什么你的代码没有'工作:因为你明确要求 SBCL使用with-{in,out}put-to-string
将I / O解释为UTF-8字符串。
另外,我想指出,您不需要像run-program
的源代码那样到达解决方案。它在SBCL's manual中清楚地记录了。