29

我正在为处理一堆文件的 Mac OS X 编写一个命令行工具。我想向用户显示正在处理的当前文件,但不希望有大量文件污染终端窗口。

相反,我想使用一行来输出文件路径,然后将该行重用于下一个文件。是否有要输出的字符(或其他代码)来std::cout完成此操作?

另外,如果我想将此工具重新定位到 Windows,两个平台的解决方案是否相同?

4

3 回答 3

32

"\r" 应该适用于 windows 和 Mac OS X。

就像是:

std::cout << "will not see this\rwill see this" << std::flush;
std::cout << std::endl; // all done
于 2010-06-16T23:49:59.500 回答
2

我无法访问 mac,但从纯控制台的角度来看,这在很大程度上取决于它如何处理回车符和换行符。如果您可以从字面上将一个或另一个发送到控制台,那么您只想发送一个回车。

我很确定 Mac 对待回车和换行的方式与 *nix & windows 不同。

如果您正在寻找就地更新(例如覆盖当前行),我建议您查看curseslib。这应该提供一个独立于平台的方法来做你正在寻找的事情。(因为,即使使用标准 C++,也没有与平台无关的方法来满足您的要求)。

于 2010-06-16T23:58:23.460 回答
0

正如 Nathan Ernst 的回答所说,如果你想要一个健壮、正确的方法来做到这一点,请使用 curses - 特别是ncurses

如果您想要一种易于使用且易于使用的 hackish 方式,请继续...

Linux、UNIX、MacOS、Windows 等的命令行终端倾向于支持一小组基本 ASCII 控制字符,包括十进制字符 13 - 称为回车,在 C++ 中编码为 '\r' 或等效的八进制 ' \015' 或十六进制 '\x0D' - 指示终端返回到行首。

你一般想做的是...

int line_width = getenv("COLUMNS") ? atoi(getenv("COLUMNS")) : 80;
std::string spaces{line_width - 1, ' '};
for (const auto& filename : filenames) {
    std::cout << '\r' << spaces << '\r' << filename << std::flush;
    process_file(filename);
}
std::cout << std::endl; // move past last filename...

这使用一串空格在写入下一个文件名之前覆盖旧文件名,因此如果您有一个较短的文件名,您不会看到较早的较长文件名的尾随字符。

std::flush确保 C++ 程序在开始处理文件之前调用 OS函数write()将文本发送到终端。没有它,更新所需的文本 - \r,空格\r和文件名 - 将被附加到缓冲区,并且仅在缓冲区已满时写入操作系统 - 例如 4k 块,因此显示的文件名将滞后数十个文件在正在处理的实际文件后面。此外,假设缓冲区是 4k - 4096 字节 - 在某些时候你有 4080 字节缓冲,然后输出下一个文件名的文本:你最终会得到\r和 15 个适合缓冲区的空格,当自动刷新时,最终会擦除屏幕上该行的前 15 个字符,并保留前一个文件名的其余部分(如果它长于 15 个字符),然后等到在更新屏幕之前缓冲区又满了(仍然是随意的)。

最后std::endl只是将光标从您一直在打印文件名的行上移开,这样您就可以写“全部完成”,或者只是离开main()并让 shell 提示符显示在一个干净的行上,而不是可能覆盖您最后一个文件名的一部分(像 zsh 这样的伟大的 shell 会检查这个)。

于 2020-05-15T21:29:21.537 回答