2015-08-09 73 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); 
}