Pipe custom stdin to system call in C++

耗尽温柔 提交于 2019-12-12 19:19:02

问题


I'm trying to call a shell script from C++ with custom input. What I could do is:

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

Of course, escaping s is quite difficult. Is there a way that I can use s as stdin for myscript.sh? Ie, something like this:

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

回答1:


A simple test to reassign stdin and restore it after the system call:

#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;
}

Works out nicely. I tested it with a simple dummy.sh script like this:

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

Note the last line dumps standard input to standard output, so you could test it like

./test <<< "hello world"

and expect the following output:

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



回答2:


Use 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);
}

You'll need to add error checking to make this robust.

Note that I prefer a const char*, since it is more flexible (works with things other than std::string) and matches what's going on inside. If you really prefer std::string, do it like so:

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

Also note that the 4096-byte buffer was chosen because it matches the page size on most systems. This isn't necessarily the most efficient approach, but it'll be fine for most purposes. I've found 32 KiB to be a sweet spot in my own unscientific tests on a laptop, so you might want to play around, but if you are serious about efficiency, you'll want to switch to asynchronous I/O, and start readn+1 immediately after initiating writen.



来源:https://stackoverflow.com/questions/12764550/pipe-custom-stdin-to-system-call-in-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!