Ocaml lwt читает стандартный вывод из другого процесса

Я пытаюсь создать новый интерфейс в Ocaml для терминального приложения. Основная идея - порождение нового процесса с Lwt:

let cmd = shell "./otherterminalapp" in
let p = open_process_full cmd;

А затем позже напишите материал в stdin процесса, чтобы выполнить команды во внешнем приложении.

 Lwt_io.write_line p#stdin "some command" >>= (fun _ -> Lwt_io.flush p#stdin)

Когда я читаю результат из команды обратно с Lwt_io.read_line_opt, Как читать, пока не осталось ни одной строки? Проблема, с которой я сталкиваюсь, заключается в том, что моя программа просто зависает в определенный момент. Когда я читаю с read_line_optв то время как я достиг конца, кажется, что он просто ждет, пока процесс перенаправит новый вывод.

Как я могу подойти к этому?

Конкретный пример того, что я пытаюсь сделать: (Приложение на основе терминала ocamldebug)

Исходный код программы:

open Lwt
open Lwt_unix
open Lwt_process
let () =
  let run () =
    let cmd = shell "ocamldebug test.d.byte" in
    let dbgr = open_process_full cmd in
    (((((((Lwt_io.write_line dbgr#stdin "info modules") >>=
            (fun _  -> Lwt_io.flush dbgr#stdin))
           >>= (fun _  -> Lwt_io.read_line_opt dbgr#stdout))
          >>=
          (fun s  ->
             (match s with
              | Some l -> print_endline l
              | None  -> print_endline "nothing here! ");
             Lwt_io.read_line_opt dbgr#stdout))
         >>=
         (fun s  ->
            (match s with
             | Some l -> print_endline l
             | None  -> print_endline "nothing here! ");
            Lwt_io.read_line_opt dbgr#stdout))
        >>=
        (fun s  ->
           (match s with
            | Some l -> print_endline l
            | None  -> print_endline "nothing here! ");
           Lwt_io.read_line_opt dbgr#stdout))
       >>=
       (fun s  ->
          (match s with
           | Some l -> print_endline l
           | None  -> print_endline "nothing here! ");
          Lwt_io.read_line_opt dbgr#stdout))
      >>=
      (fun s  ->
         (match s with
          | Some l -> print_endline l
          | None  -> print_endline "nothing here! ");
         Lwt.return ()) in
  Lwt_main.run (run ())

Если бы вы обычно бегали ocamldebug с test.d.byteВы получаете следующее в своем терминале:

    OCaml Debugger version 4.03.0

(ocd) info modules
Loading program... done.
Used modules: 
Std_exit Test Pervasives CamlinternalFormatBasics
(ocd) 

Когда я выполняю вышеуказанную программу, я получаю следующее:

    OCaml Debugger version 4.03.0

(ocd) Loading program... Used modules: 
Std_exit Test Pervasives CamlinternalFormatBasics

И вот он просто зависает..., моя программа не выходит. Даже когда я делаю Ctrl-c/Ctrl-c в моем терминале, есть активный процесс ocamlrun. Однако терминал становится отзывчивым.

Я что-то упускаю здесь очевидное?

1 ответ

Решение

Вызов Lwt.read_line_opt возвращает отложенное значение, которое будет определено в будущем как Some data как только канал читает строку, оканчивающуюся новой строкой, или None если канал был закрыт. Канал будет закрыт, если было условие конца файла. Для обычных файлов условие конца файла возникает, когда указатель файла достигает конца файла. Для каналов, которые используются для связи с подпроцессом, условие конца файла возникает, когда противоположная сторона закрывает дескриптор файла, связанный с каналом.

ocamldebug Программа не закрывает свои входы или выходы. Это интерактивная программа, которая готова взаимодействовать с пользователем в течение бесконечного периода времени или до тех пор, пока пользователь не закроет программу, либо нажав Ctrl-D или используя quit команда.

В вашем сценарии вы написали info modules Команда на входе канала. Процесс ответил тремя строками (где каждая строка - это часть данных, оканчивающаяся символом новой строки). Затем подпроцесс начал ждать следующего ввода. Вы не видите (ocd) подсказка, потому что она не заканчивается символом новой строки. Программа не зависала. Он все еще ожидает вывода от подпроцесса, а подпроцесс ожидает ввода от вас (мертвая блокировка).

Если вам действительно нужно отличать выходные данные от разных команд, то вам нужно отслеживать подсказку в выходных данных подпроцесса. Поскольку запрос не завершается переводом строки, вы не можете полагаться на read_line* семейство функций, так как они имеют линейную буферизацию. Вам необходимо прочитать все доступные символы и найти подсказку в них вручную.

С другой стороны, если вам на самом деле не нужно различать выходные данные разных команд, вы можете проигнорировать приглашение (на самом деле, вы можете даже отфильтровать его, чтобы получить лучший результат). В этом случае у вас будет две параллельные подпрограммы - одна будет отвечать за подачу входных данных, а другая будет считывать все выходные данные и выгружать их, фактически не передавая содержимое данных.

Другие вопросы по тегам