5

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-streams:octect編集:上記のテキストからは明確ではないため、少なくとも flexi-stream の場合、ストリームの要素タイプは a ( a ) であることを追加したいと思います(unsigned-byte 8)。少なくともこの場合run-program、多くの問題なしに生のバイトを読み取ることが期待されます。代わりに、次のようなメッセージが表示されますDon't know how to copy to stream of element-type (UNSIGNED-BYTE 8)

4

3 に答える 3

5

編集:この非常に単純なタスクを実行できないことに腹を立て、問題を解決しました。

機能的には、タイプ UNSIGNED-BYTE のストリームを run-program に送信して正しく機能させる機能は、私には理解できない理由で大幅に制限されています。グレー ストリーム、フレキシ ストリーム、fd ストリーム、および他のいくつかのメカニズムを試しました。

しかし、run-program のソース (5 回目または 6 回目) を熟読すると、出力に渡すことができるオプション :STREAM があることに気付きました。それを考えると、 read-byte が機能するかどうか疑問に思いました...そして機能しました。よりパフォーマンスの高い作業を行うには、ファイル以外のストリームの長さを取得して 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))))
于 2012-01-11T17:34:03.553 に答える
3

いくつかの外部ライブラリを使用する場合は、babel-streams を使用して実行できます。これは、プログラムからコンテンツを安全に取得するために使用する関数です。最初の 256 バイトを文字だけにマップするため、:latin-1 を使用します。オクテットから文字列への変換を削除して、ベクトルを取得できます。

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))                                                                                                                          
于 2012-01-12T06:47:09.240 に答える