【问题标题】:Pipe custom stdin to system call in C++将自定义标准输入管道传输到 C++ 中的系统调用
【发布时间】:2012-10-06 22:46:34
【问题描述】:

我正在尝试使用自定义输入从 C++ 调用 shell 脚本。我能做的是:

void dostuff(string s) {
    system("echo " + s + " | myscript.sh");
    ...
}

当然,转义 s 是相当困难的。有没有办法可以将 s 用作 myscript.sh 的标准输入?即,像这样:

void dostuff(string s) {
    FILE *out = stringToFile(s);
    system("myscript.sh", out);
}

【问题讨论】:

    标签: c++ file unix pipe stdin


    【解决方案1】:

    system 调用后重新分配标准输入并恢复它的简单测试:

    #include <cstdlib>     // system
    #include <cstdio>      // perror
    #include <unistd.h>    // dup2
    #include <sys/types.h> // rest for open/close
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <errno.h>
    
    #include <iostream>
    
    int redirect_input(const char* fname)
    {
        int save_stdin = dup(0);
    
        int input = open(fname, O_RDONLY);
    
        if (!errno) dup2(input, 0);
        if (!errno) close(input);
    
        return save_stdin;
    }
    
    void restore_input(int saved_fd)
    {
        close(0);
        if (!errno) dup2(saved_fd, 0);
        if (!errno) close(saved_fd);
    }
    
    int main()
    {
        int save_stdin = redirect_input("test.cpp");
    
        if (errno)
        {
            perror("redirect_input");
        } else
        {
            system("./dummy.sh");
            restore_input(save_stdin);
    
            if (errno) perror("system/restore_input");
        }
    
        // proof that we can still copy original stdin to stdout now
        std::cout << std::cin.rdbuf() << std::flush;
    }
    

    效果很好。我用这样一个简单的dummy.sh 脚本对其进行了测试:

    #!/bin/sh
    /usr/bin/tail -n 3 | /usr/bin/rev
    

    注意最后一行将标准输入转储到标准输出,所以你可以像这样测试它

    ./test <<< "hello world"
    

    并期待以下输出:

    won tuodts ot nidts lanigiro ypoc llits nac ew taht foorp //    
    ;hsulf::dts << )(fubdr.nic::dts << tuoc::dts    
    }
    hello world
    

    【讨论】:

    • 我会,除非我真的不想为此做线性(或者,如果我特别懒惰,二次)时间分配。代码也会很烦人。
    • @joshlf13 好吧,我明白了(尽管由于您提供的代码示例,您的问题意味着简短的输入)。我添加了一个小例子来说明我的想法。这对我很有效
    • 更新:包括将标准输入恢复为原始值。这种概念证明效果很好。我希望你喜欢它
    【解决方案2】:

    使用popen:

    void dostuff(const char* s) {
      FILE* f = fopen(s, "r");
      FILE* p = popen("myscript.sh", "w");
      char buf[4096];
      while (size_t n = fread(buf, 1, sizeof(buf), f))
        if (fwrite(buf, 1, n, p) < n)
          break;
      pclose(p);
    }
    

    您需要添加错误检查以使其稳健。

    请注意,我更喜欢const char*,因为它更灵活(与std::string 以外的东西一起使用)并且匹配内部发生的事情。如果您真的更喜欢std::string,请这样做:

    void dostuff(const std::string& s) {
        FILE* f = fopen(s.c_str(), "r");
        ⋮
    

    还请注意,选择 4096 字节缓冲区是因为它与大多数系统上的页面大小相匹配。这不一定是最有效的方法,但它适用于大多数用途。我发现 32 KiB 是我自己在笔记本电脑上进行的不科学测试中的最佳选择,所以你可能想玩一玩,但如果你对效率很认真,你会想要切换到异步 I/O,然后开始开始写入后立即读取n+1n

    【讨论】:

      猜你喜欢
      • 2014-05-13
      • 2019-09-03
      • 2016-04-26
      • 1970-01-01
      • 2019-05-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-21
      相关资源
      最近更新 更多