Racket:从交互式子流程输出读取所有可用数据
我想使用Racket:从交互式子流程输出读取所有可用数据,racket,Racket,我想使用子进程启动外部程序并监视其文本输出。我想检索在给定时间写入的所有数据,然后对其进行处理。然后,读取所有新数据并进行处理。这样做直到程序停止 #lang racket (match-define (list out in _ err _) (process* (find-executable-path "racket") "a.rkt")) (close-output-port in) (close-input-port err) (for/list ([
子进程
启动外部程序并监视其文本输出。我想检索在给定时间写入的所有数据,然后对其进行处理。然后,读取所有新数据并进行处理。这样做直到程序停止
#lang racket
(match-define (list out in _ err _)
(process* (find-executable-path "racket")
"a.rkt"))
(close-output-port in)
(close-input-port err)
(for/list ([line (in-lines out)])
(define len (string-length line))
(printf "processed ~a chars\n" len)
len)
(close-input-port out)
但是读取
操作似乎一直都是阻塞
。以下代码不起作用:
(let ((stream (process-stdout proc)))
(for/list ( #:when (char-ready? stream)
(line (in-lines stream)))
line)))
是否有办法读取写入子流程标准输出上的所有新数据?
编辑:我添加了一个测试代码以避免误解
#lang racket
(struct process
(stdout
stdin))
(define (start-program exe)
(define-values (s stdout stdin stderr) (subprocess #f #f #f exe))
(thread (lambda () (copy-port stderr (current-error-port))))
(process stdout stdin))
(define (send-to proc value)
(write value (process-stdin proc))
(flush-output (process-stdin proc)))
(define (receive-from proc)
(let ((stream (process-stdout proc)))
(for/list ((line (in-lines stream))) ;; TODO: seems blocking forever
(printf "RECEIVE[~a] EOF=~a~%" line (eof-object? stream)) ;; EOF is never #f
line)))
(define cmd-process (start-program "c:\\windows\\system32\\cmd.exe"))
(let loop ([x (receive-from cmd-process)])
(for ((line x))
(printf "RECEIVE:[~A]~%" x))
(flush-output)
;;
;; TODO: how to detect that EVERYTHING has been read
;; so that I can input a command such as 'dir' or whatever
;;
;; I want to avoid parsing strings to detect that I cannot read anymore.
;;
(loop (receive-from cmd-process)))
(close-input-port (process-stdout cmd-process))
(close-output-port (process-stdin cmd-process))
结果是:
RECEIVE[Microsoft Windows [Version 10.0.18362.535]] EOF=#f
RECEIVE[(c) 2019 Microsoft Corporation. All rights reserved.] EOF=#f
RECEIVE[] EOF=#f ;; <=== I want to get a #t here
RECEIVE[Microsoft Windows[Version 10.0.18362.535]]EOF=#f
接收[(c)2019 Microsoft Corporation。保留所有权利。]EOF=#f
接收[]EOF=#f 我看不出上面代码中的阻塞问题有多严重。看起来您希望程序为循环的每个迭代报告一些内容,但您的代码没有这样做
这里有一个例子,我认为它在做你想做的事情a.rkt
打印10行:
()
(0)
(0 1)
...
(0 1 2 3 4 5 6 7 8)
每次打印延迟1秒
#lang racket
(for ([i 10])
(writeln (build-list i values))
(flush-output)
(sleep 1))
现在,b.rkt
将监视a.rkt
的文本输出,“检索在给定时间写入的所有数据,然后对其进行处理。然后,读取所有新数据并对其进行处理。然后执行此操作,直到程序停止。”
上述程序输出:
processed 2 chars
processed 3 chars
processed 5 chars
processed 7 chars
processed 9 chars
processed 11 chars
processed 13 chars
processed 15 chars
processed 17 chars
processed 19 chars
并返回”(2 3 5 7 9 11 13 15 17 19)
,其中每一行一可用就立即输出。就是每一秒
请注意,外部程序(a.rkt
)需要刷新其输出。如果没有刷新,输出可以被缓冲,并且当输出仍然适合缓冲区时,您可能看不到“实时”输出。我实际上找到了一种使用事件解决问题的可能方法
#lang racket
(struct process
(process
stdout
stdin
read-event)) ;; add a read event
(define (start-program exe)
(define-values (proc stdout stdin stderr) (subprocess #f #f #f exe))
(thread (λ () (copy-port stderr (current-error-port))))
(process proc stdout stdin (read-line-evt stdout 'any))) ;; create the event
(define (send-to proc value)
(display value (process-stdin proc))
(flush-output (process-stdin proc)))
(define (read-all)
(let ((receive-string (sync/timeout 1 (process-read-event cmd-process)))) ;; read non-blocking
(when (and receive-string (not (eof-object? receive-string)))
(printf "RECV:[~A]~%" receive-string)
(read-all))))
;; MAIN
(define cmd-process (start-program "c:\\windows\\system32\\cmd.exe"))
(read-all)
(printf "SEND:[DIR]~%")
(send-to cmd-process (format "DIR~A" #\newline))
(read-all)
(printf "[WAIT FOR TERMINATE]~%")
(printf "SEND:[exit]~%")
(send-to cmd-process (format "exit~A" #\newline))
(read-all)
(subprocess-wait (process-process cmd-process))
(printf "[WAIT FOR TERMINATE] ProcessStatus=~A~%"
(subprocess-status (process-process cmd-process)))
(close-input-port (process-stdout cmd-process))
(close-output-port (process-stdin cmd-process))
此代码提供了预期的结果,无需解析字符串即可找到提示:
RECV:[Microsoft Windows [Version 10.0.18362.535]]
RECV:[(c) 2019 Microsoft Corporation. All rights reserved.]
RECV:[]
SEND:[DIR]
RECV:[E:\>DIR]
RECV:[ Volume in drive E has no label.]
RECV:[ Volume Serial Number is BE25-0CEB]
RECV:[]
RECV:[ Directory of E:\]
RECV:[]
RECV:[12/25/2019 01:18 PM <DIR> .]
RECV:[12/25/2019 01:18 PM <DIR> ..]
RECV:[ 0 File(s) 0 bytes]
RECV:[ 2 Dir(s) 163,789,271,040 bytes free]
RECV:[]
[WAIT FOR TERMINATE]
SEND:[exit]
RECV:[E:\>exit]
[WAIT FOR TERMINATE] ProcessStatus=0
RECV:[Microsoft Windows[Version 10.0.18362.535]]
记录:[(c)2019年微软公司。保留所有权利。]
记录:[]
发送:[目录]
记录:[E:\>目录]
RECV:[驱动器E中的卷没有标签。]
记录:[卷序列号为BE25-0CEB]
记录:[]
RECV:[电子目录:\]
记录:[]
记录:[2019年12月25日下午1:18]
记录:[2019年12月25日01:18下午..]
RECV:[0个文件0个字节]
RECV:[2个目录163789271040个可用字节]
记录:[]
[等待终止]
发送:[退出]
记录:[E:\>退出]
[等待终止]进程状态=0
如果有人找到一种方法来实现这种行为而不发生事件
,我会接受答案。实际上,这是一个交互式程序。提示前有一些文本需要阅读。然后你可以输入一些命令。然后你有一些命令和结果要读。等等我想知道是否有办法询问流是否从程序输出中收到了新的内容。问题之一是,一个shell命令echo 1与sleep链接10秒,chain echo 2与echo 1、enter和echo 2无法区分,除非您解析输出并查找提示。你能提供更多关于你想做什么的细节吗?您刚才添加的代码片段没有提供太多细节。send to
是否会以编程方式调用?用户将如何与程序交互?Etc.>2除非您解析输出并查找提示符,否则这正是我想要避免的,因为所有程序的提示符都不同。您的变量stream
是一个端口。它永远不会是对象的eof?
,因此部分代码肯定是错误的。另外,(for/list(#:when(char-ready?stream))
只会检查(char-ready?stream)
一次。我认为这不是你想要的。有趣的评论。你是对的,代码现在对我来说似乎非常错误。我不知道(char-ready?stream)
将被评估一次。这不是我想要的。