Stdio,cin和cout:用于unix管道的程序(如grep,sort等)

时间:2013-09-13 10:13:09

标签: c++ io pipe stdio

我想编写像unix utlities一样的程序。特别是,我想将它们与管道一起使用,例如:

grep foo myfile | ./MyTransformation [--args] | cut -f2 | ...

三个方面让我想知道如何处理I / O:

  1. 根据像Useless Use of Cat Award这样的scource,最好同时支持stdin读取和从文件中读取(在管道的开头)。这怎么做得最好?我习惯使用<getopt.h> / <cgetopt>来解析参数。我可以看到除了我的选项之外是否还有另一个文件参数并从中读取。如果没有,请从标准输入读取。这意味着如果提供inut文件,stdin将被忽略。这是否可取?

  2. 根据this question,C ++会将coutcinstdio同步,因此无法很好地缓冲。这导致性能大幅下降。解决方案是禁用同步:cin.sync_with_stdio(false);。管道中使用的程序是否始终禁用stdiocin的{​​{1}}同步?或者它应该避免使用coutcin而是使用他们自己的缓冲io形式?

  3. 由于cout将用于程序输出(除非指定了输出文件),状态消息(像%done一样的冗长)必须转到其他地方。 cout似乎是一个明显的选择。但是,状态不是错误。

  4. 总之,我想知道c ++中这些程序的io ahndling。尽管存在上述问题,仍可以使用cerr/stderrcin吗? I / O应该以不同的方式处理吗?例如,从stdin和stdout读取和写入缓冲文件是默认文件吗?实施这种行为的推荐方法是什么?

2 个答案:

答案 0 :(得分:2)

如果没有选项,标准惯用语是:

int returnCode = 0;

void
processFile( std::string const& filename )
{
    if ( filename == "-" ) {
        process( std::cin );
    } else {
        std::ifstream in( filename.c_str() );
        if ( !in.is_open() ) {
            std::cerr << argv[0] << ": cannot open " << filename << std::endl;
            returnCode = 1;
        } else {
            process( in );
        }
    }
}

int
main( int argc, char** argv )
{
    if ( argc == 1 ) {
        processFile( "-" );
    } else {
        for ( int i = 1; i != argc; ++ i ) {
            processFile( argv[i] );
        }
    }
    std::cout.flush()
    return std::cout ? returnCode : 2;
}

然而,有许多变种。我发现自己这样做了 通常我写了一个MultiFileInputStream类 (模板&gt;构造函数接受一对迭代器;然后 执行或多或少与上面相同的代码。 (全部 像往常一样,重要的代码在相应的streambuf中。) 同样,我有一个类来解析选项(看起来像 像一个不可变的std::vector<std::string>一样的选项 已被解析。所以上面会变成:

int
main( int argc, char** argv )
{
    CommandLine& args = CommandLine::instance();
    args.parse( argc, argv );
    MultiFileInputStream src( args.begin(), args.end() );
    process( src );
    return ProgramStatus::instance().returnCode();
}

ProgramStatus是另一个有用的类,它处理错误 输出和返回码。并刷新std::cout和 当您在其上调用returnCode()时调整错误代码。)

我确信编写Unix过滤程序的人已经开发出来了 类似的课程。

关于问题2:sync_with_stdio是静态的 std::ios_base的成员,因此您可以在没有对象的情况下调用它: std::ios_base::sync_with_stdio( false );。我觉得这个少了 误导,因为调用会影响所有 iostream对象。 如果IO处理是一个阻塞点,那么一定要做,但是 大多数时候,我不打扰。这种节目很少见 需要任何类型的优化。 (注意,如果你打电话 sync_with_stdio,那么你应该使用任何C风格的IO。 但无论如何我都看不出有任何理由使用它。)

关于问题3:错误消息转到std::cerr, 总是。您还希望确保返回非零回报 代码,即使错误不是致命的。类似的东西:

myprog file1 > tmp && mv tmp file1

一切都很常见,如果你有问题,那就不行了 产生输出,如果你不回来就是一场灾难 非零错误代码。 (这就是为什么我总是冲洗然后 检查std::cout的状态。很久很久以前,一个用户 我的程序做了上面的,有一个非常大的文件,和 磁盘已满。事后并没有那么充分。自那时候起: 总是冲洗std::cout,并检查它是否有效 返回OK。)

答案 1 :(得分:-1)

您确定要使用C ++吗?大多数操作系统比C ++更依赖于C和汇编。如果您要编写应用程序,那么C ++可能是一个不错的选择,但对于操作系统及其实用程序,shell和帮助程序,它们通常用C语言编写。您可以查看Linux或BSD实现以了解它用管道,标准输入和标准输出完成。如果您认为C适合您,您可以阅读Kernighan和Richie的C书“TH编程语言”,那里有很多例子,说明如何编写一个使用管道,std i / o和参数的优秀C程序。