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

Always output to screen and allow redirection

本文关键字:重定向 屏幕 输出      更新时间:2023-10-16

我正在编写一个小的 CLI 应用程序,我想允许用户重定向到文件,而标准 cout 语句转到输出.txt我希望进度始终进入屏幕。

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

这可能吗? 我该怎么做?

提前感谢!!

即使stdinstdout都被重定向,这也将起作用:

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++库的某些实现提供了用于此目的的扩展。看这里。
或者,如果您不关心可靠地获取错误代码,则可以std::ofstream terminal("/dev/tty").

此外,如果您这样做,作为设计考虑因素,提供一个安静的选项来让用户关闭对终端的写入是一个好主意。

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

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

正如@Mark在他们的评论中提到的,您可以 (ab-( 使用1std::cerr来做到这一点,以及一些 ASCII 技巧来覆盖终端上的当前输出行(查看退格字符:'b'(。


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

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

例如:

fprintf(stderr, "10%% completen");

我想出了该怎么做,即使用户重定向stderr.以下代码获取当前终端的名称,并检查我们的输出是否被重定向。它还具有my_write((函数,允许您同时写入终端和重定向文件,如果它们已重定向stdout。您可以将 my_write(( 函数与writetoterm变量一起使用,无论您想在哪里写入您希望始终写入终端的内容。extern "C"必须在那里,否则(无论如何,在带有 GCC 6.3 的 Debian 9 上(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;
}

我找到了官方的std::处理这个问题的方法。 还有另一种类型...标准::堵塞。 这是专门用于信息,并且始终显示在命令行上,即使用户将程序myProgram的输出重定向>出来.txt。

谢谢,很高兴看到可以完成的所有方法。