如果外部程序被卡住或突然请求输入(由于某些错误),是否有可能避免挂起的OCaml程序。
例如,我使用7zip通过以下代码解压缩不同格式的存档数据:
let exec_un7zip ~dpath ~fpath =
let cmdline = "7z x -aoa -o \"" ^ dpath ^ "\" \"" ^ fpath ^ "\"" in
Printf.printf "exec (%s)\n" cmdline;
let stdout = Core.Unix.open_process_in cmdline in
Core.In_channel.input_all stdout |> ignore;
stdout
如果7zip卡在某个错误上,或者尽管提供了标志,又要求其他输入,是否可以通过超时或其他方式在此处完成执行?
答案 0 :(得分:2)
OCaml的标准(带有Sys
模块)和Unix
库提供了两个可以解决问题的功能。 Core
中可能存在等效/更高级别的函数,因为您似乎正在使用它,但是我没有检查过:
Unix.alarm n
将在sigalrm
秒后发出信号n
Sys.signal Sys.sigalrm (Sys.Signal_handle (fun _ -> do_something()))
时,do_something()
将运行Sys.sigalrm
。例如,在适当的过程中调用Unix.kill
。另外,Sys.signal
返回给定信号的旧处理程序,以便您以后可以将其恢复。一个非常粗糙但未经全面测试的示例如下:
let cmd_timeout cmd args timeout =
let pid = Unix.create_process cmd args Unix.stdin Unix.stdout Unix.stderr in
let kill _ = Unix.kill pid Sys.sigterm in
let old = Sys.signal Sys.sigalrm (Sys.Signal_handle kill) in
let _ = Unix.alarm timeout in
try
let _, res = Unix.waitpid [] pid in
(match res with
| Unix.WEXITED d ->
Printf.printf "Exited with status %d\n%!" d
| Unix.WSIGNALED d ->
Printf.printf "Signalled with %d\n%!" d
| Unix.WSTOPPED d ->
Printf.printf "Stopped with %d\n%!" d);
Sys.set_signal Sys.sigalrm old
with Unix.Unix_error(Unix.EINTR, _, _) ->
Printf.printf "Timeout!\n%!"; Sys.set_signal Sys.sigalrm old
let () = cmd_timeout "sleep" [| "sleep"; "10" |] 3 (* will timeout *)
let () = cmd_timeout "sleep" [| "sleep"; "3" |] 10 (* will exit normally *)
答案 1 :(得分:1)
答案 2 :(得分:0)
我认为,如果您分配一个伪tty并将其用于stdin,那么您可以捕获从stdin中读取的命令。否则,您最好的办法是在后台启动该命令,然后在花费太长时间(不输出任何内容)时将其终止。