linux – 如何在Delphi中实时读取cygwin程序的命令行输出?

linux – 如何在Delphi中实时读取cygwin程序的命令行输出?,第1张

概述我需要阅读最初基于 Linux的Cygwin程序的冗长命令行输出.它在cmd.exe下运行良好,每几秒打印一行. 当我使用下面的代码时,在SO上多次讨论过,ReadFile函数在该程序停止之前不会返回.然后所有输出都由ReadFile提供并打印. 如何在ReadFile可用时立即读取该输出? MSDN表示,在ENABLE_LINE_INPUT模式下达到CR或缓冲区已满时,ReadFile不会返回. 我需要阅读最初基于 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:\PAS\iperf3\win32';// 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来停止程序.

如何测试所有这些:
在一个命令窗口中,启动iperf3服务器并让它监听:

D:\PAS\iperf3\win32>iperf3.exe -s -i 2 -p 5001-----------------------------------------------------------Server Listening on 5001-----------------------------------------------------------

在另一个命令窗口中,启动客户端,该客户端立即连接到服务器并每2秒开始打印输出:

D:\PAS\iperf3\win32>iperf3.exe -c 192.168.1.11 -u -b 1m -t 8 -p 5001 -l 8k -f m -i 2Connecting 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 datagramsiperf 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 datagramsiperf Done.

因此,cygwin程序输出的行为会有所不同,具体取决于它是在命令窗口还是在Delphi控制台应用程序中运行.
是的,我的输出处理代码与’line’并不完美,但让我们找出如何使Readfile实时返回,我将解决其余问题.

解决方法

How to make that output read by Readfile as soon as it is available?

问题不在您提供的代码中.它已经实时读取输出(尽管代码中存在另一个与之无关的问题,请参见下文).

您可以使用以下批处理文件而不是Cygwin可执行文件来尝试:

test.bat的:

timeout 5echo "1"timeout 5echo "2"timeout 5echo "3"

和以下bash shell文件:

test.sh:

sleep 5echo "1"sleep 5echo "2"sleep 5echo "3"

它可以实时工作,并在文本可用时立即将文本输出到控制台.

因此,如果问题不在Delphi代码中,则它与Cygwin程序有关.
我们需要有关您的Cygwin计划的更多信息,以帮助您进一步.

MSDN says that Readfile doesn’t return until CR is reached in ENABLE_liNE_input mode,or buffer full.
That progam uses linux line breaks LF,not windows CR LF.
I used small buffer 32 bytes,Disabled ENABLE_liNE_input – btw what’s the right way of disabling it?

您无需禁用它.

如果您将缓冲区设置为32个字节,那么只要缓冲区已满,Readfile函数就应该返回这32个字节,即使使用UNIX行结尾也是如此.

Maybe Readfile doesn’t return because of some other issue with cygwin program itself,not just LF line breaks?

这就是我想的.我不想猜测可能的原因,但它们与行结尾的差异无关.

是的,非windows行结尾可以使命令等待填充整个缓冲区,但不能导致Readfile阻塞.

But it works fine in windows cmd.exe,why not in Delphi console application?

好问题,这很奇怪.就我而言,它在Delphi和cmd中都有效.
这就是为什么我认为这个问题与Cygwin应用程序有关.

Also: why do we have to close this handle right after CreateProcess?
CloseHandle(StdOutPipeWrite);

这是管道的书写结束.我们不需要写句柄,因为我们不是写入管道,我们只是从它读取.
您的Cygwin应用程序间接写入该管道.

此外,代码中还有两个问题需要注意:

>您有一个line变量,其类型为string,并且未初始化.
在例程/程序的开头将其初始化为空字符串(line:=”).
>由于UNIX行以Buffer结尾,因此除非缓冲区已满,否则Readfile不会返回,因此包含多行.
您需要将对WriteLn例程的调用更改为Write并忽略行结尾,或者使用分隔行的解析器.
>行变量应该在写入stdout后清除,或者应该直接接收Buffer的值,如下所示:

...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的大小会逐渐增加,直到它包含整个输出,复制.

总结

以上是内存溢出为你收集整理的linux – 如何在Delphi中实时读取cygwin程序的命令行输出?全部内容,希望文章能够帮你解决linux – 如何在Delphi中实时读取cygwin程序的命令行输出?所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/yw/1023922.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-23
下一篇 2022-05-23

发表评论

登录后才能评论

评论列表(0条)

保存