是否有一种优雅的方法来确定ifstream是否附加到标准输入?

时间:2013-10-03 23:56:57

标签: c++ linux unix

我的程序是一个常见的shell,我试图用C ++编写。除了从命令行获取命令外,它还必须能够读取文件中的命令 - 文件名作为可选的arg传递,而不是通过重定向传递。

如果arg存在,我打开传递的文件名,否则打开“​​/ dev / stdin”。打开开发文件我并不高兴,这不是我的主要问题,但如果有人有更好的方法,我很乐意听到它。

最终我必须读取给shell的命令,但首先我必须提示我是从stdin读取的提示还是如果输入来自文件则跳过提示。我的问题是:有没有更好的方法来确定getCommand输入流是否是标准输入,而不是声明全局或传递布尔或类似的黑客攻击?

我觉得如果我能以某种方式使用std :: cin而不是打开/ dev文件,我可以将流作为istream传递。这会更容易区分这两者吗?例如。 if (source == cin)

感谢您提出的所有建议。

bool getCommand(ifstream source, std::string command)
{
    if (source == stdin)
        //print prompt to stdout

    // do the rest of stuff

    return true;
}


int main(int argc, char *argv[])
{
    std::ifstream input;
    std::string command;

    if (argc == 2)
    {
        input.open(argv[1], std::ifstream::in);

        if (! input)
        {
            perror("input command file stream open");
            exit(EXIT_FAILURE);
        }
    }
    else
    {
        input.open("/dev/stdin", std::ifstream::in);

        if (! input)
        {
            perror("input stdin stream open");
            exit(EXIT_FAILURE);
        }
    }

    //.......

    if (getCommand(input, command))
        //.......
}

2 个答案:

答案 0 :(得分:3)

如果您使用std::istream&作为变量的类型,那么您可以使用std::cin代替打开/dev/stdin

std::ifstream fileinput;
if (argc == 2)
{
    fileinput.open(argv[1], std::ifstream::in);

    if (! fileinput)
    {
        perror("input command file stream open");
        exit(EXIT_FAILURE);
    }
}
std::istream &input = (argc == 2) ? fileinput : std::cin;

你的问题的其余部分应该是关于识别tty(Detect if stdin is a terminal or pipe?)。但如果做不到这一点,一旦你做了上述改动,就可以与std::cin的地址进行比较:

if (&input == &std::cin) {
    std::cout << "prompt:";
}

显然,这不会识别命令行参数为/dev/stdin的情况,因此它有点像躲避。但我可能会认为,鉴于有趣的要求,这实际上是一种改进,因为有人至少可以写yourprogram /dev/stdin < input.txtsort input.txt | yourprogram /dev/stdin来解决你的限制并避免提示: - )

答案 1 :(得分:2)

首先,如果您想要从std::cinstd::ifstream阅读,我会根据std::istream来实施这项工作,当然要避免打开/dev/stdin

std::istream  in(std::cin.rdbuf());
std::ifstream fin;
if (argc == 2) {
    fin.open(av[1]);
    // check that the file is actually open or produce an error
    in.rdbuf(fin.rdbuf());
}
else {
    // determine if you need to create a prompt
}

if (getCommand(in, command)) {
    ...
}

现在,要确定您是否确实需要编写提示,仅确定是否正在读取标准输入,还需要确定标准输入是否连接到具有屏幕和键盘的内容。但是,没有可移植的方法。在UNIX上有像isatty()这样的函数,可用于确定文件描述符0是否连接到tty。我会使用此信息来设置iword(),然后可以使用它来检查流是否需要提示:

static int const needs_prompt = std::ios_base::xalloc();
...
in.iword(needs_prompt) = isatty(0);
...
if (in.iword(needs_prompt)) {
    std::cout << "pompt>";
}

如果您想获得幻想,可以创建一个自定义std::streambuf,它与std::ostream连接,tie()输入流流,并在{{{}}上写一个提示1}}:每次对输入流执行读取操作时,都会刷新sync() ed tie()。但是,这要求您只为每个生成的提示读取一次,例如使用std::ostream。显然,如果您不需要提示,则不要std::getline(in, line) tie()。下面是一个简单的程序,演示了自动提示方法(没有做任何其他业务):

std::ostream