如何检查程序是否正在写入终端
how to check program is writing to terminal
这是我在代码审查上发布的问题的后续 - 终端上的彩色输出,我试图在终端上输出彩色字符串并通过isatty()
调用检测它。然而,正如@Jerry棺材所指出的——
您可以使用 isatty 检查标准输出是否连接到终端,无论您要写入什么流。这意味着其余函数只有在将
std::cout
作为它们要写入的流传递时才能正常工作。否则,您可以在写入非 TTY 的内容时允许格式设置,并且可以在写入 TTY 内容时禁止格式化。
这是我不知道的事情(阅读为没有经验),我什至不知道cin/cout可以重定向到其他地方的事实。所以我试图阅读更多关于它的信息,并发现了一些关于 SO 的现有问题。这是我一起破解的内容:
// initialize them at start of program - mandatory
std::streambuf const *coutbuf = std::cout.rdbuf();
std::streambuf const *cerrbuf = std::cerr.rdbuf();
std::streambuf const *clogbuf = std::clog.rdbuf();
// ignore this, just checks for TERM env var
inline bool supportsColor()
{
if(const char *env_p = std::getenv("TERM")) {
const char *const term[8] = {
"xterm", "xterm-256", "xterm-256color", "vt100",
"color", "answersi", "cygwin", "linux"};
for(unsigned int i = 0; i < 8; ++i) {
if(std::strcmp(env_p, term[i]) == 0) return true;
}
}
return false;
}
rightTerm = supportsColor();
// would make necessary checks to ensure in terminal
inline bool isTerminal(const std::streambuf *osbuf)
{
FILE *currentStream = nullptr;
if(osbuf == coutbuf) {
currentStream = stdout;
}
else if(osbuf == cerrbuf || osbuf == clogbuf) {
currentStream = stderr;
}
else {
return false;
}
return isatty(fileno(currentStream));
}
// this would print checking rightTerm && isTerminal calls
inline std::ostream &operator<<(std::ostream &os, rang::style v)
{
std::streambuf const *osbuf = os.rdbuf();
return rightTerm && isTerminal(osbuf)
? os << "e[" << static_cast<int>(v) << "m"
: os;
}
我的主要问题是,尽管我已经手动测试了它,但我不知道这可能会失败的情况或可能包含的错误。这是做这件事的正确方法吗?我可能缺少什么吗?
下面是一个运行示例(您还需要一个包含随机数据的in.txt
):
#include <iostream>
#include <fstream>
#include <string>
#include <unistd.h>
#include <cstdlib>
#include <cstring>
void f();
bool supportsColor();
// sample enum for foreground colors
enum class fg : unsigned char {
def = 39,
black = 30,
red = 31,
green = 32,
yellow = 33,
blue = 34,
magenta = 35,
cyan = 36,
gray = 37
};
// initialize them at start of program - mandatory
// so that even if user redirects, we've a copy
std::streambuf const *coutbuf = std::cout.rdbuf();
std::streambuf const *cerrbuf = std::cerr.rdbuf();
std::streambuf const *clogbuf = std::clog.rdbuf();
// check if TERM supports color
bool rightTerm = supportsColor();
// Here is the implementation of isTerminal
// which checks if program is writing to Terminal or not
bool isTerminal(const std::streambuf *osbuf)
{
FILE *currentStream = nullptr;
if(osbuf == coutbuf) {
currentStream = stdout;
}
else if(osbuf == cerrbuf || osbuf == clogbuf) {
currentStream = stderr;
}
else {
return false;
}
return isatty(fileno(currentStream));
}
// will check if TERM supports color and isTerminal()
inline std::ostream &operator<<(std::ostream &os, fg v)
{
std::streambuf const *osbuf = os.rdbuf();
return rightTerm && isTerminal(osbuf)
? os << "e[" << static_cast<int>(v) << "m"
: os;
}
int main()
{
std::cout << fg::red << "ERROR HERE! " << std::endl
<< fg::blue << "ERROR INVERSE?" << std::endl;
std::ifstream in("in.txt");
std::streambuf *Orig_cinbuf = std::cin.rdbuf(); // save old buf
std::cin.rdbuf(in.rdbuf()); // redirect std::cin to in.txt!
std::ofstream out("out.txt");
std::streambuf *Orig_coutbuf = std::cout.rdbuf(); // save old buf
std::cout.rdbuf(out.rdbuf()); // redirect std::cout to out.txt!
std::string word;
std::cin >> word; // input from the file in.txt
std::cout << fg::blue << word << " "; // output to the file out.txt
f(); // call function
std::cin.rdbuf(Orig_cinbuf); // reset to standard input again
std::cout.rdbuf(Orig_coutbuf); // reset to standard output again
std::cin >> word; // input from the standard input
std::cout << word; // output to the standard input
return 0;
}
void f()
{
std::string line;
while(std::getline(std::cin, line)) // input from the file in.txt
{
std::cout << fg::green << line << "n"; // output to the file out.txt
}
}
bool supportsColor()
{
if(const char *env_p = std::getenv("TERM")) {
const char *const term[8] = {"xterm", "xterm-256", "xterm-256color",
"vt100", "color", "answersi",
"cygwin", "linux"};
for(unsigned int i = 0; i < 8; ++i) {
if(std::strcmp(env_p, term[i]) == 0) return true;
}
}
return false;
}
我也标记了c
语言,尽管这是c++
代码,因为相关代码是共享的黑白两个,我不想错过任何建议
OP的问题:
我的主要问题是,尽管我已经手动测试了它,但我不知道这可能会失败的情况或可能包含的错误。这是做这件事的正确方法吗?我可能缺少什么吗?
并非所有终端都支持所有功能;此外,TERM
变量最常用于选择特定的终端描述。
通常的方法是使用终端数据库,而不是硬编码。 这样做,你的方法
inline bool supportsColor()
inline std::ostream &operator<<(std::ostream &os, rang::style v)
将检查终端功能,例如,使用tigetnum
(用于颜色数量),tigetstr
(用于终端应该支持的实际转义序列)。 您可以像isatty
函数一样轻松地包装它们。
延伸阅读:
- 终端数据库接口
- 终端数据库
- 我的终端无法识别颜色(ncurses 常见问题解答)
要在 POSIX 上检查标准输出是否为终端,只需使用 isatty(3)
if (isatty(STDOUT_FILENO)) {
/// handle the stdout is terminal case
}
你也可以使用 /dev/tty
,参见 tty(4);例如,如果你的程序myprog
是在命令管道中启动的,比如./myprog some arguments | less
你仍然可以fopen("/dev/tty","w")
输出到控制终端(即使 stdout 是一个管道)。
有时,程序在没有任何控制终端的情况下运行,例如通过 crontab(5) 或 at(1)
- 如何检查程序员在C++中提供的两种不同格式的输入
- C++如何检查程序是否首次运行?
- 检查程序是手动调用还是通过系统启动调用
- 命令在终端或程序中提供不同的行为(Python和C )
- 如何使用QT检查程序是否存在于路径中
- 如何检查程序是否超过数据类型存储
- C++ 具有两个类的拼写检查程序;字典和单词
- 对简单的帐户检查程序有问题?程序返回编译错误,指出"no match for 'operator||' unsure of how to fix?"
- 如何停止任务运行终端在程序以视觉工作室代码结束后自动退出
- 在终端运行C 程序
- 如何使用Qt(C++)检查程序是否按其名称运行
- 密码检查程序-匹配密码失败-循环失败
- 如何检查程序是否由Windows服务启动和完美运行
- 如何使用 Boost.Process 0.5 运行命令行/终端实用程序
- 为来自终端的程序分配文件路径
- 检查程序是否通过另一个程序运行
- 如何检查程序是否正在写入终端
- 您如何在通过终端执行程序时获取用户输入器
- 从终端运行C++程序.在同一终端窗口中获取输出
- 如何从 Windows 控制台检查程序启动参数