2012-01-11 8 views
5

Estoy intentando ejecutar un programa externo en SBCL y capturar su salida. El resultado es datos binarios (una imagen png), mientras que SBCL insiste en interpretarlo como cadenas.Lectura de la salida binaria de un programa externo en Common Lisp

He intentado un número de maneras, como

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

Pero tengo errores como

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

Me parece que SBCL está tratando de interpretar los datos binarios como texto y decodificarlo . ¿Cómo cambio este comportamiento? Estoy interesado solo en obtener un vector de octetos.

Editar: Dado que no está claro en el texto anterior, me gustaría añadir que al menos en el caso de flexi-stream, el elemento-tipo de la secuencia es un flexi-streams:octect (que es un (unsigned-byte 8)). Esperaría que al menos en este caso run-program lea los bytes sin procesar sin muchos problemas. En cambio me sale un mensaje como Don't know how to copy to stream of element-type (UNSIGNED-BYTE 8)

Respuesta

4

Editar: Me enojé por no poder hacer esta tarea muy simple y resolvió el problema.

Funcionalmente, la posibilidad de enviar un flujo de tipo UNSIGNED-BYTE al programa de ejecución y hacer que funcione correctamente está severamente limitada, por razones que no entiendo. Intenté flujos de grises, flexi-streams, fd streams y algunos otros mecanismos, como usted.

Sin embargo, examinando la fuente del programa de ejecución (por quinta o sexta vez), noté que hay una opción: STREAM que puede pasar a la salida. Dado eso, me pregunté si el byte de lectura funcionaría ... y lo hizo. Para un trabajo más eficiente, uno puede determinar cómo obtener la longitud de una secuencia que no sea de archivo y ejecute READ-SEQUENCE en ella.

(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í, parece que funciona, ¡muchas gracias! En cualquier caso, no estoy seguro de dónde radica el problema. Quiero decir, usar una secuencia de archivos como salida funciona bien, por lo que el problema no está completamente en el programa de ejecución, sino más bien en la interacción entre una cadena de ejecución y ejecutar el programa. Pero yo esperaría que usar with-output-to-sequence funcionaría bien. De todos modos al menos tengo una solución ahora. Gracias de nuevo. –

+0

@MarcoRighele: en SO, si desea aceptar una respuesta, marca la pregunta como respondida en el sistema SO - es la marca de verificación de los botones de votación. –

+0

Si estaba esperando para ver si la otra solución también estaba funcionando. En cualquier caso, prefiero este ya que tiene menos dependencias externas. –

2

Si está dispuesto a utilizar algunas bibliotecas externas, esto se puede hacer con babel-streams. Esta es una función que uso para obtener contenido de manera segura de un programa. Uso: latin-1 porque mapea los primeros 256 bytes solo para los personajes. Puede eliminar los octetos a la cadena y tener el vector.

Si también quería stderr, podría utilizar 'anidado-salida-a-secuencia' anidado para obtener ambos.

(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

Tengo problemas para ejecutar tu ejemplo. Con SBCL en Linux recibo la advertencia: ENCODING no es una palabra clave de argumento conocida, y el funcionamiento de safe-shell me da "codificación de caracteres desconocidos: # ". Me estoy perdiendo de algo ? –

+0

No estoy del todo seguro sin las versiones conocidas de SBCL y babel que está usando. Puedes probar: iso-8859-1 también, ya que ese es el nombre canónico para ello. Asegúrese de que OCTETS-TO-STRING provenga de BABLE. –

+0

Ah sí, estaba usando sb-ext: octects-to-string. Con la función correcta y la versión más reciente de sbcl parece funcionar correctamente. Muchas gracias. –

2

Paul Nathan ya se dio una respuesta bastante completa en cuanto a cómo para leer E/S de un programa como binarios, por lo que voy a añadir qué su código no funcionó: porque explícitamente se le solicitó SBCL para interpretar la E/S como una cadena de caracteres UTF-8, usando with-{in,out}put-to-string.

Además, me gustaría señalar que no necesita llegar al código fuente run-program para llegar a la solución. Está claramente documentado en SBCL's manual.

+0

Eso es cierto para el 'con-salida-a-cadena' (que tiene como un elemento-tipo de' carácter') por supuesto, pero no para el caso flexi-stream, donde el flujo está hecho de octetos. Esperé que run-program leyera elementos del tipo 'element-type 'correcto según la transmisión, pero parece que ese no es el caso. De todos modos, ahora me doy cuenta de que los ejemplos no son muy claros, voy a poner algunos detalles más para finalizar los mensajes de error –

+0

Pero notará que no obtiene el mismo error con flexi-streams. Si observa el mensaje de error y el seguimiento de la pila, verá que una suposición razonable es que SBCL no utiliza ninguna función de escritura, sino una optimización específica de la implementación, y falla con la flexi-stream. –