linux – 如何在Delphi中实时读取cygwin程序的命令行输出?
我需要阅读最初基于
Linux的Cygwin程序的冗长命令行输出.它在cmd.exe下运行良好,每几秒打印一行.
当我使用下面的代码时,在SO上多次讨论过,ReadFile函数在该程序停止之前不会返回.然后所有输出都由ReadFile提供并打印. 如何在ReadFile可用时立即读取该输出? MSDN表示,在ENABLE_LINE_INPUT模式下达到CR或缓冲区已满时,ReadFile不会返回.该程序使用Linux换行符LF,而不是Windows CRLF.我使用32字节的小缓冲区并禁用了ENABLE_LINE_INPUT(顺便说一下,什么是禁用它的正确方法?). 也许ReadFile不会因为Cygwin程序本身的其他问题而返回,而不仅仅是LF换行?但它在Windows cmd.exe中工作正常,为什么不在Delphi控制台应用程序中呢? const CommandExe:string = 'iperf3.exe '; CommandLine:string = '-c 192.168.1.11 -u -b 1m -t 8 -p 5001 -l 8k -f m -i 2'; WorkDir:string = 'D:PASiperf3win32';// no trailing var SA: TSecurityAttributes; SI: TStartupInfo; PI: TProcessInformation; StdOutPipeRead,StdOutPipeWrite: THandle; WasOK,CreateOk: Boolean; Buffer: array[0..255] of AnsiChar;// 31 is Ok BytesRead: Cardinal; Line:ansistring; try// except with SA do begin nLength := SizeOf(SA); bInheritHandle := True; lpSecurityDescriptor := nil; end; CreatePipe(StdOutPipeRead,StdOutPipeWrite,@SA,0); try with SI do begin FillChar(SI,SizeOf(SI),0); cb := SizeOf(SI); dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES; wShowWindow := SW_HIDE; hStdInput := GetStdHandle(STD_INPUT_HANDLE); // don't redirect stdin hStdOutput := StdOutPipeWrite; hStdError := StdOutPipeWrite; end; Writeln(WorkDir+''+CommandExe+' ' + CommandLine); CreateOk := CreateProcess(nil,PChar(WideString(WorkDir+''+CommandExe+' ' + CommandLine)),True,// nil,nil,CREATE_SUSPENDED or CREATE_NEW_PROCESS_GROUP or NORMAL_PRIORITY_CLASS or CREATE_DEFAULT_ERROR_MODE,// 0,PChar(WideString(WorkDir)),SI,PI); CloseHandle(StdOutPipeWrite);// must be closed here otherwise ReadLn further doesn't work ResumeThread(PI.hThread); if CreateOk then try// finally repeat WasOK := ReadFile(StdOutPipeRead,Buffer,SizeOf(Buffer),BytesRead,nil); if BytesRead > 0 then begin Buffer[BytesRead] := #0; Line := Line + Buffer; Writeln(Line); end; until not WasOK or (BytesRead = 0); ReadLn; WaitForSingleObject(PI.hProcess,INFINITE); finally CloseHandle(PI.hThread); CloseHandle(PI.hProcess); end; finally CloseHandle(StdOutPipeRead); end; except on E: Exception do Writeln('Exception '+E.ClassName,': ',E.Message); end; 另外:为什么我们必须在CreateProcess之后立即关闭此句柄?它用于读取程序输出: CloseHandle(StdOutPipeWrite); 如果我在程序结束时关闭它,程序输出就是Ok,但是从不读取ReadLn来停止程序. 如何测试所有这些: D:PASiperf3win32>iperf3.exe -s -i 2 -p 5001 ----------------------------------------------------------- Server listening on 5001 ----------------------------------------------------------- 在另一个命令窗口中,启动客户端,该客户端立即连接到服务器并每2秒开始打印输出: D:PASiperf3win32>iperf3.exe -c 192.168.1.11 -u -b 1m -t 8 -p 5001 -l 8k -f m -i 2 Connecting to host 192.168.1.11,port 5001 [ 4] local 192.168.1.11 port 52000 connected to 192.168.1.11 port 5001 [ ID] Interval Transfer Bandwidth Total Datagrams [ 4] 0.00-2.00 sec 240 KBytes 0.98 Mbits/sec 30 [ 4] 2.00-4.00 sec 240 KBytes 0.98 Mbits/sec 30 [ 4] 4.00-6.00 sec 248 KBytes 1.02 Mbits/sec 31 [ 4] 6.00-8.00 sec 240 KBytes 0.98 Mbits/sec 30 - - - - - - - - - - - - - - - - - - - - - - - - - [ ID] Interval Transfer Bandwidth Jitter Lost/Total Datagrams [ 4] 0.00-8.00 sec 968 KBytes 0.99 Mbits/sec 0.074 ms 0/121 (0%) [ 4] Sent 121 datagrams iperf Done. 服务器也与客户端一起打印输出: Accepted connection from 192.168.1.11,port 36719 [ 5] local 192.168.1.11 port 5001 connected to 192.168.1.11 port 52000 [ ID] Interval Transfer Bandwidth Jitter Lost/Total Datagrams [ 5] 0.00-2.00 sec 240 KBytes 983 Kbits/sec 0.052 ms 0/30 (0%) [ 5] 2.00-4.00 sec 240 KBytes 983 Kbits/sec 0.072 ms 0/30 (0%) [ 5] 4.00-6.00 sec 248 KBytes 1.02 Mbits/sec 0.077 ms 0/31 (0%) [ 5] 6.00-8.00 sec 240 KBytes 983 Kbits/sec 0.074 ms 0/30 (0%) [ 5] 8.00-8.00 sec 0.00 Bytes 0.00 bits/sec 0.074 ms 0/0 (nan%) - - - - - - - - - - - - - - - - - - - - - - - - - [ ID] Interval Transfer Bandwidth Jitter Lost/Total Datagrams [ 5] 0.00-8.00 sec 0.00 Bytes 0.00 bits/sec 0.074 ms 0/121 (0%) ----------------------------------------------------------- Server listening on 5001 ----------------------------------------------------------- 因此,iperf3客户端在命令窗口中运行良好.现在让我们在客户端模式下启动“我的”代码,而iperf3服务器仍在监听.服务器接受连接并开始打印输出 Accepted connection from 192.168.1.11,port 36879 [ 5] local 192.168.1.11 port 5001 connected to 192.168.1.11 port 53069 [ ID] Interval Transfer Bandwidth Jitter Lost/Total Datagrams [ 5] 0.00-2.00 sec 240 KBytes 983 Kbits/sec 0.033 ms 0/30 (0%) [ 5] 2.00-4.00 sec 240 KBytes 983 Kbits/sec 0.125 ms 0/30 (0%) [ 5] 4.00-6.00 sec 248 KBytes 1.02 Mbits/sec 0.106 ms 0/31 (0%) [ 5] 6.00-8.00 sec 240 KBytes 983 Kbits/sec 0.109 ms 0/30 (0%) [ 5] 8.00-8.00 sec 0.00 Bytes 0.00 bits/sec 0.109 ms 0/0 (nan%) - - - - - - - - - - - - - - - - - - - - - - - - - [ ID] Interval Transfer Bandwidth Jitter Lost/Total Datagrams [ 5] 0.00-8.00 sec 0.00 Bytes 0.00 bits/sec 0.109 ms 0/121 (0%) ----------------------------------------------------------- Server listening on 5001 ----------------------------------------------------------- 这意味着iperf3客户端是在“我的”代码中启动的,但它不会打印任何东西!只有在客户端完成后,’my’代码才会输出以下内容: Connecting to host 192.168.1.11,port 5001 [ 4] local 192.168.1.11 port 53069 connected to 192.168.1.11 port 5001 [ ID] Interval Transfer Bandwidth Total Datagrams [ 4] 0.00-2.00 sec 240 KBytes 0.98 Mbits/sec 30 [ 4] 2.00-4.00 sec 240 KBytes 0.98 Mbits/sec 30 [ 4] 4.00-6.00 sec 248 KBytes 1.02 Mbits/sec 31 [ 4] 6.00-8.00 sec 240 KBytes 0.98 Mbits/sec 30 - - - - - - - - - - - - - - - - - - - - - - - - - [ ID] Interval Transfer Bandwidth Jitter Lost/Total Datagrams [ 4] 0.00-8.00 sec 968 KBytes 0.99 Mbits/sec 0.109 ms 0/121 (0%) [ 4] Sent 121 datagrams iperf Done. 因此,cygwin程序输出的行为会有所不同,具体取决于它是在命令窗口还是在Delphi控制台应用程序中运行. 解决方法
问题不在您提供的代码中.它已经实时读取输出(尽管代码中存在另一个与之无关的问题,请参见下文). 您可以使用以下批处理文件而不是Cygwin可执行文件来尝试: test.bat的: timeout 5 echo "1" timeout 5 echo "2" timeout 5 echo "3" 和以下bash shell文件: test.sh: sleep 5 echo "1" sleep 5 echo "2" sleep 5 echo "3" 它可以实时工作,并在文本可用时立即将文本输出到控制台. 因此,如果问题不在Delphi代码中,则它与Cygwin程序有关.
您无需禁用它. 如果您将缓冲区设置为32个字节,那么只要缓冲区已满,ReadFile函数就应该返回这32个字节,即使使用UNIX行结尾也是如此.
这就是我想的.我不想猜测可能的原因,但它们与行结尾的差异无关. 是的,非Windows行结尾可以使命令等待填充整个缓冲区,但不能导致ReadFile阻塞.
好问题,这很奇怪.就我而言,它在Delphi和cmd中都有效.
这是管道的书写结束.我们不需要写句柄,因为我们不是写入管道,我们只是从它读取. 此外,代码中还有两个问题需要注意: >您有一个Line变量,其类型为string,并且未初始化. ... Buffer[BytesRead] := #0; Line := Buffer; // <- Assign directly to Line,do not concatenate // TODO: Use a parser to separate the multiple lines // in `Line` and output then with `WriteLn` or // ignore line endings altogether and just use `Write` Write(Line); ... 除非你这样做,Line的大小会逐渐增加,直到它包含整个输出,复制. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |