问题
I've noticed that a lot of command line tools, wget for example, will show progress as a number or progress bar that advances as a process is completed. While the question isn't really language-specific, out of the languages I use most often for command line tools (C++, Node.js, Haskell) I haven't seen a way to do this.
Here's an example, three snapshots of a single line of Terminal as wget downloads a file:
Along with other information, wget shows a progress bar (<=>) that advances as it downloads a file. The amount of data downloaded so far (6363, 179561, 316053) and the current download speed (10.7KB/s, 65.8KB/s, 63.0KB/s) update as well. How is this done?
Ideally, please include a code sample from one or more of the three languages mentioned above.
回答1:
Just print a CR (without a newline) to overwrite a line. Here is an example program in perl:
#!/usr/bin/env perl
$| = 1;
for (1..10) {
print "the count is: $_\r";
sleep(1)
}
I've also disabled output buffering ($| = 1) so that the print command sends its output to the console immediately instead of buffering it.
Haskell example:
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
回答2:
Looking at GNU wget repo on GitHub -- progress.c
It seems they do it the same way i.e. print a \r and then overwrite.
/* 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);
}
回答3:
I can only speak about node.js, but the built-in readline module has some very basic screen handling functionality built-in. For example:
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);
There are also third party modules if you want to get fancier, such as multimeter/meterbox and blessed/blessed-contrib.
Generally speaking though, some programs use ncurses, while others simply just manually output the ANSI escape codes to clear and redraw the current line.
回答4:
They probably use the fancy ncurses library but on my Linux for my personal command-line tools I simply send '\r' to move the cursor back to the start of the line to overwrite it with new progress information.
#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;
}
来源:https://stackoverflow.com/questions/31900566/how-do-command-line-tools-change-their-output-after-outputting-it