2012-01-11 14 views
5

Sto tentando di eseguire un programma esterno in SBCL e di acquisire l'output. L'output è dati binari (un'immagine png), mentre SBCL insiste a interpretarlo come stringhe.Lettura dell'uscita binaria di un programma esterno in Common Lisp

Ho provato un certo numero di modi, come

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

ma ricevo errori come

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

Mi sembra che SBCL sta cercando di interpretare i dati binari come un testo e decodificarlo . Come cambio questo comportamento? Mi interessa solo ottenere un vettore di ottetti.

Edit: Dato che non è chiaro dal testo di cui sopra, vorrei aggiungere che, almeno nel caso di flexi-stream, l'elemento-tipo del torrente è un flexi-streams:octect (che è un (unsigned-byte 8)). Mi aspetterei almeno in questo caso run-program di leggere i byte non elaborati senza molti problemi. Invece ricevo un messaggio come Don't know how to copy to stream of element-type (UNSIGNED-BYTE 8)

risposta

4

Modifica: Mi sono arrabbiato per non essere in grado di fare questo compito molto semplice e risolto il problema.

Funzionalmente, la possibilità di inviare un flusso di tipo UNSIGNED-BYTE in run-program e farlo funzionare correttamente è severamente limitata, per ragioni che non capisco. Ho provato i flussi grigi, i flussi flessibili, i flussi fd e alcuni altri meccanismi, come te.

Tuttavia, l'origine del programma di esecuzione (per la quinta o la sesta volta), ho notato che c'è un'opzione: STREAM che puoi trasmettere all'output. Detto questo, mi chiedevo se il byte di lettura avrebbe funzionato ... e così è stato. Per un lavoro più performante, è possibile determinare come ottenere la lunghezza di un flusso non file ed eseguire READ-SEQUENCE su di esso.

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

Sì, questo sembra funzionare, grazie mille! In ogni caso non sono sicuro di dove si trova il problema. Voglio dire, usare un flusso di file come output funziona bene, quindi il problema non è interamente nel programma di esecuzione, ma piuttosto nell'interazione tra un flusso di stringhe e un programma di esecuzione. Ma mi aspetterei che l'uso di con output-to-sequence funzionerebbe correttamente. Almeno ora ho una soluzione ora. Grazie ancora. –

+0

@MarcoRighele: su SO, se si cura di accettare una risposta, segna la domanda come risposta nel sistema SO - è il segno di spunta dai pulsanti di voto. –

+0

Se era in attesa di vedere se anche l'altra soluzione funzionava. In ogni caso preferisco questo perché ha meno dipendenze esterne. –

2

Se si desidera utilizzare alcune librerie esterne, è possibile farlo con babel-stream. Questa è una funzione che utilizzo per ottenere in modo sicuro il contenuto da un programma. Io uso: latin-1 perché esegue il mapping dei primi 256 byte solo ai caratteri. Puoi rimuovere gli ottetti in corda e avere il vettore.

Se si desiderava anche stderr, è possibile utilizzare annidato "con-uscita-in-sequenza" per ottenere entrambi.

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

Ho problemi nell'esecuzione del tuo esempio. Con SBCL sotto linux ottengo l'avviso: ENCODING non è una parola chiave argomento noto., E la shell sicura in esecuzione mi dà "Codifica caratteri sconosciuti: # ". Mi sto perdendo qualcosa ? –

+0

Non completamente sicuro senza conoscere le versioni di SBCL e Babel che stai utilizzando. Puoi provare anche con iso-8859-1, visto che è il nome canonico per questo. Assicurati che OCTETS-TO-STRING provenga da BABLE. –

+0

Ah sì, stavo usando sb-ext: octects-to-string. Con la funzione corretta e la versione più recente di sbcl sembra funzionare correttamente. Grazie molto. –

2

Paul Nathan già dato una risposta abbastanza completa da come per leggere di I/O da un programma come binario, quindi mi limiterò a aggiungere perché il codice non ha funzionato: perché si chiesto esplicitamente a SBCL di interpretare l'I/O come una stringa di caratteri UTF-8, utilizzando with-{in,out}put-to-string.

Inoltre, vorrei sottolineare che non c'è bisogno di andare fino a codice sorgente run-program s' per arrivare alla soluzione. È chiaramente documentato in SBCL's manual.

+0

Questo vale per 'with-output-to-string' (che ha come elemento-tipo di' carattere'), ovviamente, ma non per il caso flexi-stream, in cui il flusso è fatto di ottetti. Mi aspettavo che il programma di esecuzione leggesse elementi del giusto 'tipo di elemento 'a seconda del flusso, ma sembra che non sia il caso. Ad ogni modo, mi rendo conto ora che gli esempi non sono molto chiari, inserirò qualche altro dettaglio nei messaggi di errore –

+0

Ma noterete che non si ottiene lo stesso errore con i flexi-stream. Se si guarda il messaggio di errore e la traccia dello stack, si noterà che SBCL non utilizza alcuna funzione di scrittura ma qualche ottimizzazione specifica dell'implementazione e non riesce con il flusso flessibile. –