2015-08-09 131 views
12

我注意到很多命令行工具,例如wget,都会显示进度,因为数字或进度条在进程完成时会前进。虽然这个问题并不是特定于语言的,但我最常使用的语言是命令行工具(C++,Node.js,Haskell),但我还没有看到这样做的方法。命令行工具在输出后如何更改其输出?

下面是一个例子,终端的作为wget的一个单线的三个快照下载文件:Beginning processMiddle of processProcess nearly complete

随着其他信息,wget的显示一个进度条(< =>),该前进,因为它下载文件。目前下载的数据量(6363,179561,316053)和当前下载速度(10.7KB /秒,65.8KB /秒,63.0KB /秒)也更新。这是如何完成的?

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

+0

我猜他们使用原生API – yizzlez

+5

他们可能使用[ncurses的(http://invisible-island.net/ncurses/) –

+2

Haskell有一个好的,简单'ncurses' API:HTTPS:/ /hackage.haskell.org/package/ncurses – dfeuer

回答

12

只需打印一个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 
3

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

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

尽管如此,一些程序使用ncurses,而其他程序只是手动输出ANSI转义码来清除并重绘当前行。

1

他们可能使用了花式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; 
} 
5

在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); 
}