Вывод подпроцесса в строку в Racket

#subprocess #racket

#подпроцесс #racket

Вопрос:

Как бы вы реализовали следующую процедуру в Racket:

(command->string "printf" "hello world")

таким образом, чтобы:

  • Он запускает argv[] '("printf" "hello world") в подпроцессе, находя printf в PATH .
  • Если подпроцесс завершается с нулевым кодом, возвращает стандартный вывод в виде строки.
  • Если подпроцесс завершается ненулевым, возникает исключение.
  • Stdin и stderr игнорируются; они такие же, как для процесса Racket.

Комментарии:

1. Не забудьте опубликовать написанный вами код 😉

Ответ №1:

Вы можете использовать

 (with-output-to-string
  (lambda ()
    (or (system* (find-executable-path "printf") "hello world")
        (error 'who "command failed"))))
  

Если вы попробуете это в Racket REPL, это сработает, но, похоже, это сбивает с толку REPL, и следующее приглашение не отображается. Я думаю, что, возможно printf , команда просматривает stdin, или, возможно, в XREPL есть ошибка. Обертывание приведенного выше выражения с помощью

 (parameterize ((current-input-port (open-input-string ""))) _)
  

кажется, проблема устранена.

Комментарии:

1. Спасибо. Я получаю такое же поведение. Понятия не имею, что это вызывает.

2. Как вообще with-output-to-string работать с подпроцессами? (with-output-to-string (lambda () (writeln (file-stream-port? (current-output-port)) (current-error-port)))) выдает #f .

3. Я думаю system* , что настраивает вспомогательные потоки для перемещения данных между текущими портами Racket и портами файлового потока, созданными subprocess . Поэтому system* не имеет значения, какой порт является текущим выходным портом; он просто скопирует на него данные из порта файлового потока, созданного для подпроцесса.

Ответ №2:

 (define (command->string command . args)
  (let-values (((sub stdout stdin stderr)
                (apply subprocess #f
                       (current-input-port)
                       (current-error-port)
                       (find-executable-path command)
                       args)))
    (let ((output (port->string stdout)))
      (subprocess-wait sub)
      (if (eqv? 0 (subprocess-status sub))
          output
          (error "Command failed:" (cons command args))))))

(writeln (command->string "printf" "%s" "hello world"))
  

На случай, если кто-нибудь из Racket читает, пожалуйста, подумайте о добавлении чего-то подобного в стандартную библиотеку. Это просто и довольно часто требуется.

Комментарии:

1. Возможно, можно было бы создать проблему на их общедоступном Github? Это довольно открытое сообщество.

2. Это не работает из REPL, потому subprocess что требуется, чтобы stdin был портом файла (который, я полагаю, в более широком смысле означает «порт fd» в Unix). REPL current-input-port не является файловым портом.

3. FWIW: это было объединено всего пару дней назад. github.com/racket/racket/pull/3468 . Это будет в документации для Racket 7.10 / 8.0. Хотя это также несколько простой пример.