Чтение двоичного вывода внешней программы в Common Lisp - PullRequest
5 голосов
/ 11 января 2012

Я пытаюсь запустить внешнюю программу в SBCL и записать ее вывод.Выходные данные - двоичные данные (изображение в формате png), в то время как SBCL настаивает на интерпретации их как строк.*

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

Мне кажется, что SBCL пытается интерпретировать двоичные данные как текст и декодировать их.Как мне изменить это поведение?Меня интересует только получение вектора октетов.

Редактировать: поскольку из вышеприведенного текста не ясно, я хотел бы добавить, что, по крайней мере, в случае flexi-stream, element-тип потока - flexi-streams:octect (который (unsigned-byte 8)).Я ожидаю, что по крайней мере в этом случае run-program будет читать необработанные байты без особых проблем.Вместо этого я получаю сообщение типа Don't know how to copy to stream of element-type (UNSIGNED-BYTE 8)

Ответы [ 3 ]

4 голосов
/ 11 января 2012

Редактировать: Я разозлился из-за того, что не смог выполнить эту очень простую задачу, и решил проблему.

Функционально возможность отправки потока типа UNSIGNED-BYTE в run-program и правильной его работы строго ограничена по причинам, которые я не понимаю. Я пробовал серые потоки, flexi-потоки, потоки fd и некоторые другие механизмы, такие как вы.

Однако, просматривая источник запуска программы (в пятый или шестой раз), я заметил, что есть опция: 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))))
2 голосов
/ 09 августа 2013

Пол Натан уже дал довольно полный ответ на вопрос , как считывать ввод / вывод из программы в двоичном виде, поэтому я просто добавлю , почему ваш код не работал: потому что вы явно просили SBCL интерпретировать ввод-вывод как строку символов UTF-8, используя with-{in,out}put-to-string.

Также я хотел бы отметить, что вы неЧтобы найти решение, нужно пройти исходный код run-program.Это четко задокументировано в руководстве SBCL .

2 голосов
/ 12 января 2012

Если вы хотите использовать некоторые внешние библиотеки, это можно сделать с помощью 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))                                                                                                                          
...