我正在嘗試做一些非常具體的事情,包括將控制字符發送到標準輸出并從標準輸入讀取。
我在 Python 中有一個有效的實作,我正在嘗試將其轉換為 OCaml。
令我驚喜的是,它可以非常直接地翻譯,幾乎是逐行翻譯。但是當我運行它時,行為是不同的,OCaml 不起作用。
在我看來,問題一定是 OCaml 和 Python 運行時處理終端的方式之間存在一些模糊的差異,也許是標準輸入。
首先這里是作業 Python 代碼:
import os, select, sys, time, termios, tty
def query_colours():
fp = sys.stdin
fd = fp.fileno()
if os.isatty(fd):
old_settings = termios.tcgetattr(fd)
tty.setraw(fd)
try:
print('\033]10;?\07\033]11;?\07')
r, _, _ = select.select([ fp ], [], [], 0.1)
if fp in r:
return fp.read(48)
else:
print("no input available")
return None
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
else:
raise ValueError("Not a tty")
我的 OCaml 翻譯看起來像:
let query_colours () =
let fd = Unix.stdin in
if Unix.isatty fd then
let old_settings = Unix.tcgetattr fd in
set_raw fd;
Fun.protect
~finally:(fun () -> Unix.tcsetattr fd Unix.TCSADRAIN old_settings)
(fun () ->
print_string "\o033]10;?\o007\o033]11;?\o007";
let r, _, _ = Unix.select [fd] [] [] 0.1 in
let buf = Bytes.create 48 in
Printf.printf ">> len r: %d\n" (List.length r); (* debugging *)
ignore @@ (
match List.exists (fun (el) -> el == fd) r with
| true -> Unix.read fd buf 0 48
| false -> failwith "No input available"
);
Bytes.to_string buf
)
else
invalid_arg "Not a tty"
請注意,我們必須對tty.setraw. 首先,這里是 Python stdlib 的源代碼:
def setraw(fd, when=TCSAFLUSH):
"""Put terminal into a raw mode."""
mode = tcgetattr(fd)
mode[IFLAG] = mode[IFLAG] & ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON)
mode[OFLAG] = mode[OFLAG] & ~(OPOST)
mode[CFLAG] = mode[CFLAG] & ~(CSIZE | PARENB)
mode[CFLAG] = mode[CFLAG] | CS8
mode[LFLAG] = mode[LFLAG] & ~(ECHO | ICANON | IEXTEN | ISIG)
mode[CC][VMIN] = 1
mode[CC][VTIME] = 0
tcsetattr(fd, when, mode)
iflag, oflag, cflag,lflag是位掩碼整數
In OCaml side the Stdlib has provided instead of four bit-masked ints a single record with all the boolean values: https://ocaml.org/api/Unix.html#TYPEterminal_io
My OCaml translation of tty.setraw looks like:
let set_raw ?(set_when=Unix.TCSAFLUSH) fd =
let mode : Unix.terminal_io = {
(Unix.tcgetattr fd) with
c_brkint = false;
c_icrnl = false;
c_inpck = false;
c_istrip = false;
c_ixon = false;
c_opost = false;
c_csize = 8;
c_parenb = false;
c_echo = false;
c_icanon = false;
(* c_iexten = false; ...does not exist on Unix.terminal_io *)
c_ixoff = false; (* IEXTEN and IXOFF appear to set the same bit *)
c_isig = false;
c_vmin = 1;
c_vtime = 0;
} in
Unix.tcsetattr fd set_when mode
Ok, now the problem...
When I run the Python version it just returns a string like:
'\x1b]10;rgb:c7f1/c7f1/c7f1\x07\x1b]11;rgb:0000/0000/0000\x07'
which is the intended behaviour. I do not hear the BEL sound or any other content printed to screen.
When I run my OCaml version, I hear the BEL sound and I see:
╰─ dune exec -- ./bin/cli.exe
>> len r: 0
Fatal error: exception Failure("No input available")
^[]10;rgb:c7f1/c7f1/c7f1^G^[]11;rgb:0000/0000/0000^G%
╭─ ? ? ~/Documents/Dev/ *5 !4 ?4 2 ? 18:20:26 ?
╰─ 10;rgb:c7f1/c7f1/c7f1
╭─ ? ? ~/Documents/Dev/ *5 !4 ?4 2 ? 18:20:26 ?
╰─ 11;rgb:0000/0000/0000
We can see from the print debugging len r: 0 that the select call did not find stdin ready for reading.
Instead we see the results sent to stdin in the terminal after my program has exited.
FWIW,如果我從 OCaml 程式中運行 Python 腳本,Unix.open_process_in那么我會從 Python 腳本中得到相同的(損壞的)行為:
utop # run "bin/query.py";;
- : string list = ["\027]10;?\007\027]11;?\007"; "no input available"]
我意識到這可能是一個不起眼的角落,但如果有人有任何見識,我將不勝感激。
uj5u.com熱心網友回復:
這是要閱讀的大量代碼,但僅從描述來看,聽起來您正在將終端回傳到其舊狀態,然后再重繪 輸出。
這對 OCaml 來說并不是什么特別奇怪的事情,但 OCaml 確實傾向于比其他一些語言更長時間地保持緩沖輸出。
您可以嘗試在以下內容之后添加此內容print_string:
flush stdout
就像我說的,有很多代碼要閱讀,這只是我的快速入門。
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/426443.html
