很长一段时间以来,我对终端在类 Unix 系统中,它启动 shell 进程并通过其与它通信来为其提供用户界面标准输入,标准输出&标准错误。
然而最近在研究通过 cygwin 终端启动 Windows 控制台应用程序的问题时,我意识到它可能不是那么简单。
在http://cygwin.com/1.5/cygwin-ug-net/using-effectively.html我懂了,
另一个问题是从基于控制台的 Windows 程序接收输出或向其提供输入。遗憾的是,与 Windows 控制台应用程序交互并不是使用转换实用程序的简单问题。Windows 控制台应用程序设计为在 command.com 或 cmd.exe 下运行,有些应用程序不能很好地处理其他情况。Cygwin 只有在控制台(DOS 框)中运行时才能接收控制台输入,因为 Windows 不提供任何连接到控制台设备后端的方法。另一种传统的 Unix 输入/输出方法 ptys(伪终端)受 Cygwin 支持,但 Windows 并不完全支持。基本问题是 Cygwin pty 是一个管道,有些 Windows 应用程序不喜欢将其输入或输出重定向到管道。
我写了一个小 C 程序,并在 Windows 下使用 VC++ 进行了编译进程管理器-
#include <stdio.h>
int main(int argc, char *argv[]) {
#define BUFFER_LEN 1024
char buffer[BUFFER_LEN];
printf("echo server started\n");
while (fgets(buffer, BUFFER_LEN, stdin) != NULL) {
printf("%s", buffer);
}
return 0;
}
当我运行 Cygwin 终端时(执行程序)并启动该程序,我无法与其交互 -
[puneet@freestyle ~]$ /cygdrive/c/echo1.exe
Hello
^-- 没有回应
但当我把它放进管道里时,它就起作用了——
[puneet@freestyle ~]$ echo -e "1\n2\n3" | /cygdrive/c/echo1.exe | while read line; do echo $line; done
1
2
3
[puneet@freestyle ~]$
基本上它无法与执行程序终端。然而当运行执行程序直接从 Windows 控制台,它可以正确地与 - 进行交互
[puneet@freestyle ~]$ /cygdrive/c/echo1.exe
Hello
Hello
^Z
[puneet@freestyle ~]$
然后我想如果我远程控制'd 进入我的机器并运行这个程序作为命令,它会工作,因为终端不会直接与它交互,但 SSH 服务器会。然而这也行不通 -
[puneet@freestyle ~]$ ssh freestyle /cygdrive/c/echo1.exe
Hello
^-- 没有回应
但是把它放进管道里又可以了!-
[puneet@freestyle ~]$ echo -e "1\n2\n3" | ssh freestyle /cygdrive/c/echo1.exe | while read line; do echo $line; done
1
2
3
[puneet@freestyle ~]$
有人能解释一下这些观察背后的理论吗?
终端和 shell 之间的交互是否不仅仅是使用 shell 的标准输入,标准输出和标准错误?
Windows 控制台有何不同?为什么 Windows 控制台程序在与 cygwin 程序的管道中似乎运行良好?
答案1
如果您在循环fflush(stdout)
中添加while
(在之后printf
),那么您的程序将按预期运行,即使在 mintty 中也是如此。您还应该能够将调用setbuf(stdout, NULL)
作为您在 stdout 上执行的第一个操作,并使其运行。
您还可以在 Windows 控制台窗口内运行 c:\cygwin\bin\bash,您的原始程序将按预期运行。我还没有尝试过,但您也应该能够在 conemu 或 console2 窗口内运行 bash,并让您的原始程序按预期运行。
也就是说:这是关于薄荷的。
以下是正在发生的事情(部分):
Windows 控制台非常特殊。Windows 有一个名为客户端/服务器运行时子系统的服务。当您在 Windows 上启动 cmd 提示符时,您实际上是在连接到客户端/服务器运行时子系统,并且它正在为您创建窗口。
另一方面,Mintty 是一个“常规”窗口(恰好运行终端模拟器)。
Microsoft 的 C 库 i/o 例程实际上会测试它们是否在客户端/服务器运行时子系统创建的窗口下运行,如果是,则更改其行为。它们所做的一项更改是关闭 stdout 的完全缓冲。
当您在 mintty 下运行时,Windows 的 gets/puts 例程认为它们没有在命令提示符中运行,因此它们正在进行完全缓冲。
这里还有一个技巧:在bash
运行 under时,mintty
您可以运行cat | echo1.exe
。然后输入一些内容,当您点击时,^D
所有缓冲的 stdout 输出将同时显示出来。
这个cat | echo1.exe
技巧之所以有效,是因为这cat
是一个 cygwin 程序。Cygwin 本质上是一个 posix 模拟库。因此stdin
ofcat
是由 Cygwin 库模拟的,而 Cygwin 库的处理方式^D
与“真正的”Windows stdin 不同。
但是如果你只是在under underecho1.exe
下运行,那么你的按键将进入非模拟文件流(即,你的 stdin 是真正的 Windows stdin,而不是 Cygwin 库模拟的 stdin),因此不会发送 eof。相反,它会发送 eof,但对 bash 来说意味着一些特殊的东西,所以它不会被发送。相反,你需要立即终止(并且不会刷新缓冲区,这是正确的。)bash
mintty
^D
^Z
^Z
^C
echo1.exe