从C++调用外部命令时如何保留其输出的颜色?
保留命令颜色输出的解决方案
问题核心:绝大多数支持颜色输出的命令会检测输出目标是否为交互式终端(TTY),当通过popen创建的管道输出时,命令会判定输出不是终端,自动关闭颜色渲染。要保留颜色,有两种可行方案:
方案1:给目标命令添加强制颜色参数
很多命令自带强制开启颜色的参数,按需添加即可:
ls:--color=alwaysgrep:--color=alwaysgit:--color=alwaysdocker:部分子命令支持--color=always
示例调用:
exec("ls --color=always /tmp");
方案2:用工具模拟TTY环境(通用方案)
使用unbuffer(属于expect工具集,需先安装)或script命令,让目标命令误以为运行在交互式终端中,从而输出颜色控制码。
基于unbuffer的函数修改
int exec(const string &command) { std::array<char, 128> buffer; std::string result; // 用unbuffer包裹原命令,模拟TTY环境 std::string wrapped_cmd = "unbuffer " + command + " 2>&1"; std::shared_ptr<FILE> pipe(popen(wrapped_cmd.c_str(), "r"), pclose); if (!pipe) { std::cerr << "管道创建失败" << std::endl; return -1; } while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) { result += buffer.data(); } if (!result.empty()) { std::cout << result << std::endl; } // 修复原代码bug:避免pclose被调用两次 int status = pclose(pipe.get()); pipe.reset(); return WEXITSTATUS(status); }
基于script的函数修改(无额外依赖)
如果系统没有unbuffer,可以用默认自带的script命令:
// 替换wrapped_cmd的拼接逻辑 std::string wrapped_cmd = "script -q -c \"" + command + "\" /dev/null 2>&1";
-q为静默模式,/dev/null用于丢弃script自身的日志输出。
额外修复说明
原代码存在bug:std::shared_ptr已将pclose设为销毁回调,最后再次调用pclose(pipe.get())会导致pclose被执行两次,触发未定义行为。上述修改中已通过手动调用pclose后重置指针解决,并使用WEXITSTATUS解析正确的命令退出状态码。
内容的提问来源于stack exchange,提问作者ellipticaldoor
相关产品推荐
相关产品推荐

