为什么重定向会在管道发生故障的地方工作?

时间:2016-04-21 23:00:32

标签: windows command-line

理论上,这两个命令行应该是等价的:

1

type tmp.txt | test.exe

2

test.exe < tmp.txt

我有一个涉及#1的过程,多年来,工作得很好;在去年的某个时刻,我们开始使用较新版本的Visual Studio编译程序,现在由于输入格式错误而失败(见下文)。但#2成功(没有例外,我们看到了预期的输出)。为什么#2会在#1失败的情况下成功?

我已经能够将test.exe减少到以下程序。我们的输入文件每行只有一个标签,并统一使用CR / LF行结尾。所以这个程序永远不应该写入stderr:

#include <iostream>
#include <string>

int __cdecl main(int argc, char** argv)
{
    std::istream* pIs = &std::cin;
    std::string line;

    int lines = 0;
    while (!(pIs->eof()))
    {
        if (!std::getline(*pIs, line))
        {
            break;
        }

        const char* pLine = line.c_str();
        int tabs = 0;
        while (pLine)
        {
            pLine = strchr(pLine, '\t');
            if (pLine)
            {
                // move past the tab
                pLine++;
                tabs++;
            }
        }

        if (tabs > 1)
        {
            std::cerr << "We lost a linebreak after " << lines << " good lines.\n";
            lines = -1;
        }

        lines++;
    }

    return 0;
}

当通过#1运行时,我得到以下输出,每次都有相同的数字(在每种情况下,它都是因为getline返回了两个连接的行,没有干预的换行符);当通过#2运行时,(正确)没有输出:

We lost a linebreak after 8977 good lines.
We lost a linebreak after 1468 good lines.
We lost a linebreak after 20985 good lines.
We lost a linebreak after 6982 good lines.
We lost a linebreak after 1150 good lines.
We lost a linebreak after 276 good lines.
We lost a linebreak after 12076 good lines.
We lost a linebreak after 2072 good lines.
We lost a linebreak after 4576 good lines.
We lost a linebreak after 401 good lines.
We lost a linebreak after 6428 good lines.
We lost a linebreak after 7228 good lines.
We lost a linebreak after 931 good lines.
We lost a linebreak after 1240 good lines.
We lost a linebreak after 2432 good lines.
We lost a linebreak after 553 good lines.
We lost a linebreak after 6550 good lines.
We lost a linebreak after 1591 good lines.
We lost a linebreak after 55 good lines.
We lost a linebreak after 2428 good lines.
We lost a linebreak after 1475 good lines.
We lost a linebreak after 3866 good lines.
We lost a linebreak after 3000 good lines.

1 个答案:

答案 0 :(得分:4)

结果是known issue

  

该错误实际上是在低级_read函数中,stdio   库函数(包括fread和fgets)用于从a读取   文件描述符。

     

_read中的错误如下:如果......

     
      
  1. 您正在阅读文字模式管道
  2.   
  3. 你调用_read读取N个字节,
  4.   
  5. _read成功读取N个字节,
  6.   
  7. 读取的最后一个字节是回车符(CR),
  8.         

    然后_read函数将成功完成读取但是会   返回N-1而不是N.结尾处的CR或LF字符   结果缓冲区不计入返回值。

         

    在此bug中报告的特定问题中,fread调用_read来填充   流缓冲区。 _read报告它填充了N-1个字节   缓冲区和最终的CR或LF字符丢失。

         

    该bug基本上是时间敏感的,因为_read是否可以   从管道中成功读取N个字节取决于数据有多少   被写入管道。更改缓冲区大小或更改时间   刷新缓冲区可能会减少问题的可能性,但它   在100%的情况下,不一定会解决问题。

         

    有几种可行的解决方法:

         
        
    1. 使用二进制管道并执行文本模式CRLF =&gt;在阅读器端手动翻译LF。这并不是特别困难   (扫描缓冲区中的CRLF对;用一个LF替换它们。)
    2.   
    3. 使用_osfhnd(fh)调用ReadFile,完全绕过读取器端的CRT I / O库(尽管这也需要手动   文本模式转换,因为OS不会进行文本模式转换   你)
    4.         

      我们已修复此错误,以便下次更新Universal CRT。注意   Universal CRT是一个操作系统组件   独立于Visual C ++库提供服务。下一次更新   通用CRT可能与大约相同的时间框架   Windows 10周年更新今年夏天。