How do command line tools change their output after outputting it?

后端 未结 4 960
北恋
北恋 2021-02-13 18:24

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\

相关标签:
4条回答
  • 2021-02-13 18:34

    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
    
    0 讨论(0)
  • 2021-02-13 18:50

    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.

    0 讨论(0)
  • 2021-02-13 18:51

    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;
    }
    
    0 讨论(0)
  • 2021-02-13 18:55

    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);
    }
    
    0 讨论(0)
提交回复
热议问题