从 cygwin shell 启动时写入 stdout 的位置在哪里,没有重定向
Posted
技术标签:
【中文标题】从 cygwin shell 启动时写入 stdout 的位置在哪里,没有重定向【英文标题】:Where do writes to stdout go when launched from a cygwin shell, no redirection 【发布时间】:2010-10-26 21:55:01 【问题描述】:我有一个应用程序,我们称之为 myapp.exe,它是双模式控制台/GUI,构建为 /SUBSYSTEM:WINDOWS(有一个 3KB 的小垫片 myapp.com 导致 cmd.exe 等待显示新的提示。)
如果我从命令提示符启动:
myapp
-> cmd.exe 运行 myapp.com,它运行 myapp.exe。 stdout 最初是一个分离的控制台,通过使用AttachConsole
和freopen("CONOUT$", "w", stdout)
,我的输出出现在命令框中。好的
myapp.exe
-> cmd.exe 显示提示过早(已知问题),否则同上。不是正常的使用场景。
myapp > log
-> stdout 是一个文件,std::cout
的正常使用会在文件中结束。好的
如果我从 Windows 资源管理器启动:
myapp.com
-> 控制台已创建,标准输出为控制台,输出进入控制台。与对整个程序使用 /SUBSYSTEM:CONSOLE 的结果相同,除了当myapp.com
是控制台中的唯一进程时我添加了一个暂停。不是正常的使用场景。
myapp.exe
-> stdout 是一个 NULL 句柄,我检测到这一点并将 std::cout
挂钩到 GUI。好的
如果我从 Matlab shell 启动:
system('myapp')
或 system('myapp.com')
或 system('myapp.exe')
-> 对于所有三种变体,标准输出通过管道传输到 MatLab。好的
如果我从 cygwin bash shell 启动:
./myapp.com
-> 就像从 cmd.exe 启动一样,输出出现在命令框中。好的
./myapp
->(bash 找到 ./myapp.exe
)。 这是破案子。 stdout 是一个非 NULL 句柄,但输出无处可去。这是从 bash 运行程序的正常情况,需要修复!
./myapp > log
-> 就像从带有文件重定向的 cmd.exe 启动一样。好的
./myapp | cat
-> 类似于文件重定向,除了输出在控制台窗口上结束。好的
有谁知道 cygwin 在启动 /SUBSYSTEM:WINDOWS 进程时将什么设置为标准输出以及如何将std::cout
绑定到它?或者至少告诉我如何找出我从GetStdHandle(STD_OUTPUT_HANDLE)
那里得到的句柄?
我的程序是用 Visual C++ 2010 编写的,没有/clr
,以防万一。操作系统是 Windows 7 64 位。
编辑:要求提供更多信息。
CYGWIN 环境变量为空(或不存在)。
GetFileType()
返回FILE_TYPE_UNKNOWN
。 GetLastError()
返回 6 (ERROR_INVALID_HANDLE)
。在调用AttachConsole()
之前或之后检查都没有关系。
但是,如果我只是忽略无效句柄和freopen("CONOUT$", "w", stdout)
,那么一切正常。我只是错过了一种区分(破坏的)控制台输出和文件重定向的方法,GetFileType()
提供了这一点。
编辑:最终代码:
bool is_console(HANDLE h)
if (!h) return false;
::AttachConsole(ATTACH_PARENT_PROCESS);
if (FILE_TYPE_UNKNOWN == ::GetFileType(h) && ERROR_INVALID_HANDLE == GetLastError())
/* workaround cygwin brokenness */
h = ::CreateFile(_T("CONOUT$"), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (h)
::CloseHandle(h);
return true;
CONSOLE_FONT_INFO cfi;
return ::GetCurrentConsoleFont(h, FALSE, &cfi) != 0;
bool init( void )
HANDLE out = ::GetStdHandle(STD_OUTPUT_HANDLE);
if (out)
/* stdout exists, might be console, file, or pipe */
if (is_console(out))
#pragma warning(push)
#pragma warning(disable: 4996)
freopen("CONOUT$", "w", stdout);
#pragma warning(pop)
//std::stringstream msg;
//DWORD result = ::GetFileType(out);
//DWORD lasterror = ::GetLastError();
//msg << result << std::ends;
//::MessageBoxA(NULL, msg.str().c_str(), "GetFileType", MB_OK);
//if (result == FILE_TYPE_UNKNOWN)
// msg.str(std::string());
// msg << lasterror << std::ends;
// ::MessageBoxA(NULL, msg.str().c_str(), "GetLastError", MB_OK);
//
return true;
else
/* no text-mode stdout, launch GUI (actual code removed) */
【问题讨论】:
这是使用默认的 Cygwin 控制台(通过“Cygwin Bash Shell”快捷方式启动)吗?您在 CYGWIN 环境变量中是否有任何内容,尤其是tty
? GetFileType()
可以告诉你你正在处理什么样的句柄。
@ak2: GetFileType
只是门票。如果你回答我会接受。
我在 Console2 中使用 cygwin 时也有类似的行为,但使用“Cygwin64 终端”快捷方式就可以了。 Console2 的 shell 设置为 C:\cygwin64\bin\zsh.exe --login -i -c "cd ~;exec /bin/zsh"
使用 AttachConsole(-1) 而不是 AttachConsole(windowProcessId) 为我修复了它,但我的情况略有不同,在 C# 中
@JamesEJ:你在我的代码中哪里看到windowProcessId
?我使用了符号常量ATTACH_PARENT_PROCESS
,它是一个等于(unsigned)-1
的枚举标志,而不是进程ID。
【参考方案1】:
GetFileType() 函数允许区分某些类型的句柄,特别是控制台、管道、文件和损坏的句柄。
【讨论】:
以上是关于从 cygwin shell 启动时写入 stdout 的位置在哪里,没有重定向的主要内容,如果未能解决你的问题,请参考以下文章
从 Cygwin 启动 python IDLE 时 Tkinter 中的 tcl_error
Cygwin下安装fugitive.vim插件时vim启动极慢
Cygwin/X x11 从 Raspberry Pi 转发到 Windows