从 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 最初是一个分离的控制台,通过使用AttachConsolefreopen("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_UNKNOWNGetLastError() 返回 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 环境变量中是否有任何内容,尤其是 ttyGetFileType() 可以告诉你你正在处理什么样的句柄。 @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启动时进入指定目录

Cygwin下安装fugitive.vim插件时vim启动极慢

Cygwin/X x11 从 Raspberry Pi 转发到 Windows

从shell中更新/写入到文档的数字,会变为float类型,怎么解决

可以在 EmacsW32 和 cygwin 中使用 tramp 吗?