【问题标题】:Stdio, cin and cout: Programs for use in unix pipes (like grep, sort, etc)Stdio、cin 和 cout:用于 unix 管道的程序(如 grep、sort 等)
【发布时间】:2013-09-13 10:13:08
【问题描述】:

我想编写类似于 unix 实用程序的程序。特别是,我想将它们与管道一起使用,例如:

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

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

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

  2. 根据this question,C++ 将coutcinstdio 同步,因此不能很好地缓冲。这会导致性能大幅下降。一个解决方案是禁用同步:cin.sync_with_stdio(false);。管道中使用的程序是否应该始终禁用与stdiocincout 的同步?还是应该避免使用cincout 而是使用它们自己的缓冲io 形式?

  3. 由于cout 将用于程序输出(除非指定了输出文件),因此状态消息(详细程度,如完成百分比)必须转到其他位置。 cerr/stderr 似乎是一个显而易见的选择。但是,状态不是错误。

总之,我想知道 C++ 中此类程序的 io 和处理。尽管存在上述问题,可以使用cincout 吗?是否应该以不同的方式处理 I/O?例如,读取和写入缓冲文件,其中 stdin 和 stdout 是默认文件?实现这种行为的推荐方法是什么?

【问题讨论】:

    标签: c++ io pipe stdio


    【解决方案1】:

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

    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 类 (template> 构造函数接受一对迭代器;然后 执行或多或少与上面相同的代码。 (全部 像往常一样,重要的代码在相应的流缓冲区中。) 同样,我有一个类来解析选项(看起来 就像一个不可变的 std::vector&lt;std::string&gt; 一旦选项 已被解析。所以上面会变成:

    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 和 2。关于问题 3,我正在考虑“冗长”,例如警告或文件完成量的百分比。我已经看到程序“滥用”stderr,但我不确定这是否是好的做法,或者我是否应该完全避免这样的输出
    • @b.buchhold 默认情况下,Unix 过滤程序并不冗长。大多数时候,我要么完全避免这样的输出,要么让它依赖于一个选项(在这种情况下,它转到std::cerr)。在某些情况下,您可能会使其依赖于isatty,在这种情况下,std::cout 也可能是合适的(因为如果您要输出到管道,isatty 将是错误的)。但一般规则是,只要一切正常,什么都不说。
    【解决方案2】:

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

    【讨论】:

    • 我不想编写操作系统实用程序。相反,我想编写适用于大型文本文件(高达 TB)的应用程序。其中大部分是 I/O 绑定的(并行化问题不大)并且可以按顺序完成。到目前为止,我使用了几个程序在给定输入和输出文件(组织在具有依赖关系的 Makefile 中)的情况下进行转换,但我意识到管道(和 tee)非常适合我的操作链。我的转换不是微不足道的,包括 stl 模板(如 vector 或 google 的密集哈希图)的 C++ 非常有用
    • 操作系统内核的某些部分必须在汇编程序中。但是今天从来没有任何理由使用 C。 (在内核代码中,您可能不想使用 C++ 的所有功能,但如果他在谈论 std::cinstdin,他显然不是在内核代码中编程。)当然,Linux 中的大多数实用程序都是不是真正的 Linux,而是 GNU,并且是在 C++ 可用之前编写的。
    • 这并不能真正回答问题。此外,“使用C 优于C++ 用于操作系统及其实用程序”的观点也是没有根据的。例如 GCC:beta.slashdot.org/story/173381
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-05-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多