2012-10-06 24 views
2

我正尝试从C++调用具有自定义输入的shell脚本。我能做的就是:在C++中管道自定义标准输入到系统调用

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

当然,逃避s是相当困难的。有没有一种方法可以将s用作myscript.sh的stdin?也就是说,这样的事情:

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

回答

2

一个简单的测试,以重新分配标准输入和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 
+0

我想,除非我真的不想做直链(或者,如果我特别懒,二次),只是那个时间分配。代码也会很烦人。 – joshlf

+0

@ joshlf13嗯,我明白了(虽然你的问题意味着短的输入,由于你给的代码示例)。我已经添加了一个我想到的例子。这对我很好用 – sehe

+1

更新:包括恢复stdin回原始值。这个概念证明工作得很好。我希望你喜欢它 – sehe

0

使用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 + 1 n