有没有办法擦除最后一行输出?

时间:2015-10-04 20:45:04

标签: node.js command-line-interface ansi

一个打印3行输出的非常简单的程序:

console.log('a');
console.log('b');
console.log('c');

程序是否有一种方法可以删除打印后的最后一行?,即

console.log('a');
console.log('b');
console.log('c');
eraseLastLine();
console.log('C');

会产生:

a
b
C

6 个答案:

答案 0 :(得分:15)

简单的解决方案是不打印换行符(即不要使用console.log)。

  • 使用process.stdout.write打印不带EOL字符的行。
  • 使用回车符(\r)字符返回到行的开头。
  • 使用\e[K清除光标位置到行尾的所有字符。

示例:

process.stdout.write("000");
process.stdout.write("\n111");
process.stdout.write("\n222");

对于这一行,输出将是:

000
111
222

但是,如果执行:

process.stdout.write("000");
process.stdout.write("\n111");
process.stdout.write("\n222");
process.stdout.write("\r\x1b[K")
process.stdout.write("333");

输出结果为:

000
111
222\r\x1b[K
333

但是,终端将显示:

000
111
333

答案 1 :(得分:5)

方式一:

const clearLastLine = () => {
  process.stdout.moveCursor(0, -1) // up one line
  process.stdout.clearLine(1) // from cursor to end
}

方式 2:

const readline = require('readline')

const clearLastLine = () => {
  readline.moveCursor(process.stdout, 0, -1) // up one line
  readline.clearLine(process.stdout, 1) // from cursor to end
}

方式 3:

const ESC = '\x1b' // ASCII escape character
const CSI = ESC + '[' // control sequence introducer

const clearLastLine = () => {
  process.stdout.write(CSI + 'A') // moves cursor up one line
  process.stdout.write(CSI + 'K') // clears from cursor to line end
}

方式 1 使用内置 tty 模块提供的方法。

方式 2 使用内置的 readline 模块。

方式 3 使用 ANSI escape sequences

我通过查看 ANSI escape sequences 的源代码了解了 log-update,这是我从 dthreeanswer 中了解到的。在做了更多研究之后,我了解了内置的 readlinetty 模块。

通过阅读Node.js的源代码,我了解到方式1中使用的方法本质上是对方式2中使用的方法的包装,而方式2中使用的方法本质上是对中使用的方法的包装方式 3. 所以每一种方式最终都会在引擎盖下做基本相同的事情。上级到下级的方式我已经排序了。

我建议使用方式 1 替代方式 2 和方式 2 替代方式 3,主要是因为我认为方式 1 比方式 2 代码更少且更清晰(因此更具可读性),而方式 2 代码更少且更清晰(因此比方法 3 更具可读性,并且通过使用较低级别的方法实现的性能改进(如果有)可能可以忽略不计。但是,我认为方法 3 避免加载内置的 readline 模块。

这个解决方案(每一种方式)都假设您的光标位于程序调用 clearLastLine() 之前的空行的开头。正如 Gajus 中的 his answer 所述,如果您的光标位于最后一行的末尾,则解决方案会更简单。那么您可以使用 process.stdout.write()(而不是 console.log())来打印没有尾随换行符的最后一行吗?

或者,如果您不需要首先打印最后一行,则解决方案甚至更简单。所有这些都是在修改标准输出 code smell 吗?你能重新设计你的代码,这样最后一行就不会被打印出来吗?

就我而言,我正在测试我编写的调用 console.error() 的辅助函数,并且我不希望记录的错误消息显示在我的测试输出中。因此,与其让我的辅助函数调用 console.error(),我还可以让它抛出错误(或者只返回错误代码和/或消息或布尔值)。然后,如果我的辅助函数抛出错误(或返回错误代码和/或消息或 false),则调用我的辅助函数的主程序可能会调用 console.error()。或者我可以将一个布尔值 flag argument 传递给我的辅助函数,告诉它是否记录错误。或者我可以重定向 stderr,或 mock 全局 consolestub console.error()

如果您想清除写入 system.stdout 的最后三行,您可以这样做:

const clearLastLines = (count) => {
  process.stdout.moveCursor(0, -count)
  process.stdout.clearScreenDown()
}

clearLastLines(3)

答案 2 :(得分:4)

以下适用于我:

process.stdout.clearLine();
process.stdout.cursorTo(0); 

答案 3 :(得分:2)

您可以使用logUpdate NPM模块或vorpal NPM模块。这些都是这样做的。

答案 4 :(得分:0)

有一个简单的解决方法:

process.stdout.write(`\x1Bc\rData left: ${currentPercentage}%`)

那将打印出类似以下内容: “剩余数据:100%” ...,然后使用它,您将在每次迭代后清除屏幕。

'\ x1Bc'清除行,并且'\ r'返回第0列。

答案 5 :(得分:0)

唯一对我有用的模块是single-line-log

安装:

npm i single-line-log

导入:

var log = require('single-line-log').stdout;

然后不用console.log()记录进度条,而是使用log()

// OLD:
while (task_is_running) {
    console.log(Progress.update())
}

// NEW:
while (task_is_running) {
    log(Progress.update())
}

结合 clui 作为进度条,效果很好。