始终输出到屏幕并允许重定向

时间:2017-08-18 23:07:44

标签: c++ c

我正在编写一个小型CLI应用程序,我希望允许用户重定向到文件,而标准cout语句转到output.txt文件,我希望进展到屏幕。

./myApp > output.txt
10% complete
...
90% complete
Completed

这可能吗?我该怎么办?

提前致谢!!

5 个答案:

答案 0 :(得分:1)

即使重定向 stdin stdout ,这也会有效:

spectras@etherbee:~$ ./term
hello terminal!
spectras@etherbee:~$ ./term >/dev/null 2>&1
hello terminal!

这个想法是直接打开进程的控制终端,绕过任何重定向,如下所示:

#include <errno.h>
#include <fcntl.h>
#include <unistd.h>

int main()
{
    int fd = open("/dev/tty", O_WRONLY);
    if (fd < 0 && errno != ENODEV) {
        /* something went wrong */
        return 1;
    }
    int hasTTY = (fd >= 0);

    if (hasTTY) {
        write(fd, "hello terminal!\n", 16);
    }
    return 0;
}

来自man 4 tty

  

文件/ dev / tty是一个主编号为5的字符文件   次要编号0,通常为模式0666和owner.group root.tty。它是   进程控制终端的同义词,如果有的话。

如果您正在使用C ++,您可能希望将文件描述符包装到自定义streambuf中,因此您可以在其上使用常规流API。或者,C ++库的某些实现为此提供了扩展。见here
或者,如果您不关心可靠地获取错误代码,则可以std::ofstream terminal("/dev/tty")

另外作为设计考虑,如果你这样做,提供一个安静的选项,让用户关闭写到终端是一个好主意。

答案 1 :(得分:0)

您的进程无法知道shell是否重定向标准控制台输出(None)。

因此,您需要另一个句柄,让您可以独立于该重定向输出到终端。

正如their comment中提到的@Mark你可以(ab-)使用 1 std::cout这样做,以及一些ASCII技巧来覆盖当前的输出行终端(查看退格字符:std::cerr)。

1) 如果输出实际上没有被重定向,更不用说在终端上打印的混乱了。

答案 2 :(得分:0)

即使用户重定向stderr,我也想出了如何做到这一点。以下代码获取当前终端的名称,并检查我们的输出是否被重定向。它还有一个my_write()函数,允许您写入终端和重定向文件,如果他们已重定向stdout。您可以将my_write()函数与writetoterm变量一起使用 - 您希望在哪里编写要将始终写入终端的内容。 extern "C" ,否则(无论如何,在Debian 9上使用GCC 6.3)ttyname()函数将一直返回NULL

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <iostream>
#include <string>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>
#include <error.h>
#include <errno.h>
#include <sstream>

using std::string;
using std::fstream;
using std::cout;
using std::endl;
using std::cerr;
using std::stringstream;

void my_write(bool writetoterm, int termfd, string data)
{
    if(writetoterm)
    {
        int result = write(termfd, data.c_str(), data.length());
        if(result < data.length()){
            cerr << "Error writing data to tty" << endl;
        }
    }
    cout << data;
}

extern "C" {
char* GetTTY(int fd){
    //printf("%s", ttyname(fd));
    return ttyname(fd);
}
}

int main(int argc, char** argv){
    getenv("TTY");
    bool writetoterm = false;
    struct stat sb = {};

    if(!GetTTY(STDOUT_FILENO)){
        //not a TTY
        writetoterm = true;
    }
    int ttyfd = open(GetTTY(2), O_WRONLY);
    if(ttyfd < 0){
        //error in opening
        cout << strerror(errno) << endl;
    }
    string data = "Hello, world!\n";
    my_write(true, ttyfd, data);

    int num_for_cout = 42;
    stringstream ss;
    ss << "If you need to use cout to send something that's not a string" << endl;
    ss << "Do this: " << num_for_cout << endl;
    my_write(writetoterm, ttyfd, ss.str().c_str());

    return 0;
}

答案 3 :(得分:0)

您可以将进度指示符编写到stderr流。如果用户将stdout重定向到文件,它们将出现在控制台上。

例如:

fprintf(stderr, "10%% complete\n");

答案 4 :(得分:0)

我找到了处理此问题的官方std ::方法。还有另一种类型...... std :: clog。这是专门针对信息的,并且总是出现在命令行中,即使用户重定向程序的输出myProgram&gt; out.txt。

非常感谢能够看到所有这些方法。