Frage Lesen der binären Ausgabe eines externen Programms in Common Lisp


Ich versuche, ein externes Programm in SBCL auszuführen und seine Ausgabe zu erfassen. Die Ausgabe besteht aus Binärdaten (ein PNG-Bild), während SBCL darauf besteht, sie als Zeichenfolgen zu interpretieren.

Ich habe verschiedene Wege versucht, wie

(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))

Aber ich bekomme Fehler wie

Illegal :UTF-8 character starting at byte position 0.

Es scheint mir, dass SBCL versucht, die Binärdaten als Text zu interpretieren und zu dekodieren. Wie ändere ich dieses Verhalten? Ich bin nur daran interessiert, einen Vektor von Oktetten zu erhalten.

Edit: Da es aus dem obigen Text nicht klar ist, möchte ich hinzufügen, dass zumindest im Falle von Flexi-Stream der Elementtyp des Streams a flexi-streams:octect (die ein (unsigned-byte 8)). Ich würde zumindest in diesem Fall erwarten run-program um die rohen Bytes ohne viele Probleme zu lesen. Stattdessen bekomme ich eine Nachricht wie Don't know how to copy to stream of element-type (UNSIGNED-BYTE 8)


5
2018-01-11 08:51


Ursprung


Antworten:


Edit: Ich wurde wütend, dass ich diese sehr einfache Aufgabe nicht machen konnte und löste das Problem.

Funktionell ist die Fähigkeit, einen Stream vom Typ UNSIGNED-BYTE in das Run-Programm zu senden und korrekt zu arbeiten, aus Gründen, die ich nicht verstehe, stark eingeschränkt. Ich habe graue Streams, Flexi-Streams, Fd-Streams und einige andere Mechanismen wie Sie ausprobiert.

Als ich jedoch die Quelle des Run-Programms (zum fünften oder sechsten Mal) durchging, bemerkte ich, dass es eine Option gibt: STREAM, die Sie an die Ausgabe übergeben können. Angesichts dessen fragte ich mich, ob Read-Byte funktionieren würde ... und das tat es. Für leistungsfähigere Arbeit könnte man ermitteln, wie man die Länge eines Nicht-Datei-Streams erhält und READ-SEQUENCE darauf laufen lässt.

(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))))

4
2018-01-11 17:34



Wenn Sie bereit sind, einige externe Bibliotheken zu verwenden, können Sie dies mit Babel-Streams tun. Dies ist eine Funktion, mit der ich Inhalte aus einem Programm sicher abrufen kann. Ich benutze: latin-1, weil es die ersten 256 Bytes nur auf die Zeichen abbildet. Sie könnten die Oktette zu einer Zeichenfolge entfernen und den Vektor haben.

Wenn Sie auch stderr wollten, könnten Sie geschachtelte 'mit-Ausgabe-zu-Sequenz' verwenden, um beide zu erhalten.

(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
2018-01-12 06:47



Paul Nathan hat bereits eine ziemlich vollständige Antwort gegeben Wie I / O von einem Programm als Binär zu lesen, also werde ich einfach hinzufügen Warum Dein Code hat nicht funktioniert: weil du ausdrücklich gefragt SBCL, um die E / A als Zeichenfolge von UTF-8-Zeichen zu interpretieren, mit with-{in,out}put-to-string.

Ich möchte auch darauf hinweisen, dass Sie nicht so weit gehen müssen run-program's Quellcode, um zur Lösung zu kommen. Es ist deutlich dokumentiert in SBCL's Handbuch.


2
2017-08-09 16:10