命令行工具在输出后如何更改其输出?

我注意到,很多命令行工具,例如wget,将会显示进度作为一个数字或进度条,随着进程的完成而提前。 虽然这个问题并不是特定于语言的,但是我最常使用的语言是命令行工具(C ++,Node.js,Haskell)。我还没有看到这样做的方法。

下面是一个例子,作为wget下载文件的一行terminal的三个快照: 开始的过程 进程中间 过程几乎完成

除了其他信息,wget还显示了一个进度条(<=>),它在下载文件时会有所进步。 目前下载的数据量(6363,179561,316053)和当前的下载速度(10.7KB /秒,65.8KB /秒,63.0KB /秒)也更新。 这是怎么做的?

理想情况下,请包含上述三种语言中的一种或多种语言的代码示例。

只需打印一个CR(不带换行符)来覆盖一行。 这里是perl中的一个示例程序:

 #!/usr/bin/env perl $| = 1; for (1..10) { print "the count is: $_\r"; sleep(1) } 

我也禁用了输出缓冲( $| = 1 ),以便打印命令立即将其输出发送到控制台,而不是缓冲它。

哈斯克尔例子:

 import System.IO import Control.Monad import Control.Concurrent main = do hSetBuffering stdout NoBuffering forM_ [1..10] $ \i -> do putStr $ "the count is: " ++ show i ++ "\r" threadDelay 1000000 

看GitHub上的GNU wget回购 – progress.c

看来他们是这样做的,即打印一个\r然后覆盖。

 /* Print the contents of the buffer as a one-line ASCII "image" so that it can be overwritten next time. */ static void display_image (char *buf) { bool old = log_set_save_context (false); logputs (LOG_VERBOSE, "\r"); logputs (LOG_VERBOSE, buf); log_set_save_context (old); } 

我只能说node.js,但内置的readline模块有一些非常基本的内置屏幕处理function。 例如:

 var readline = require('readline'); var c = 0; var intvl = setInterval(function() { // Clear entirety of current line readline.clearLine(process.stdout, 0); readline.cursorTo(process.stdout, 0); process.stdout.write('Progress: ' + (++c) + '%'); if (c === 100) clearInterval(intvl); }, 500); 

还有第三方模块,如果你想获得更多的发明,如multimeter / meterboxblessed / blessed-contrib meterbox

一般来说,有些程序使用ncurses,而其他程序只是手动输出ANSI转义码来清除和重绘当前行。

他们可能使用了一些奇特的ncurses库,但是在我的Linux用于我的个人命令行工具,我只是发送'\r'来将光标移回到行的开头,用新的进度信息覆盖它。

 #include <thread> #include <chrono> #include <iostream> int main() { for(auto i = 0; i < 100; ++i) { std::cout << "\rprogress: " << i << "% " << std::flush; std::this_thread::sleep_for(std::chrono::milliseconds(100)); } std::cout << "\rprogress: DONE " << std::flush; }