【问题标题】:In-place progress output in the terminal or console终端或控制台中的就地进度输出
【发布时间】:2011-01-21 03:27:05
【问题描述】:

当您运行git clone 时,它会及时更新进度。例如,对象收到就地变化的百分比。

user@athena:~/cloj/src$ git clone git://git.boinkor.net/slime.git
Initialized empty Git repository in /home/user/cloj/src/slime/.git/
remote: Counting objects: 15936, done.
remote: Compressing objects: 100% (5500/5500), done.
Receiving objects:  28% (4547/15936), 3.16 MiB | 165 KiB/s

这是如何完成的?它是使用 ncurses 还是更简单的东西,例如退格字符和常规字符输出的某种组合?

我对如何通过 Ruby 实现这种控制台输出特别感兴趣。

编辑

我最初的问题得到了回答。但这里有一个附录。例如,当您使用 MPlayer 时,它不仅会更新一行以显示当前进度,还会更新 previous 行(例如,当您按下暂停时)。

 =====  PAUSE  =====
A:  79.9 (01:19.9) of 4718.0 ( 1:18:38.0)  0.3% 

如何就地更新两行输出?

【问题讨论】:

标签: ruby unix terminal ncurses


【解决方案1】:

我为多行输出更新编写了一个小类:

class ConsoleReset
  # Unix
  # Contains a string to clear the line in the shell
  CLR = "\e[0K"
  # ANSI escape sequence for hiding terminal cursor
  ESC_CURS_INVIS = "\e[?25l"
  # ANSI escape sequence for showing terminal cursor
  ESC_CURS_VIS   = "\e[?25h"
  # ANSI escape sequence for clearing line in terminal
  ESC_R_AND_CLR  = "\r#{CLR}"
  # ANSI escape sequence for going up a line in terminal
  ESC_UP_A_LINE = "\e[1A"

  def initialize
    @first_call = true
  end

  def reset_line(text = '')
    # Initialise ANSI escape string
    escape = ""

    # The number of lines the previous message spanned
    lines = text.strip.lines.count - 1

    # Clear and go up a line
    lines.times { escape += "#{ESC_R_AND_CLR}#{ESC_UP_A_LINE}" }

    # Clear the line that is to be printed on
    # escape += "#{ESC_R_AND_CLR}"

    # Console is clear, we can print!
    STDOUT.print escape if !@first_call
    @first_call = false
    print text
  end

  def hide_cursor
    STDOUT.print(ESC_CURS_INVIS)
  end

  def show_cursor
    STDOUT.print(ESC_CURS_VIS)
  end

  def test
    hide_cursor

    5.times do |i|
      line = ['===========================================']
      (1..10).each do |num|
        line << ["#{num}:\t#{rand_num}"]
      end
      line << ['===========================================']
      line = line.join("\n")
      reset_line(line)
      sleep 1
    end

    show_cursor

    puts ''
  end

  private
    def rand_num
      rand(10 ** rand(10))
    end
end

灵感来自prydonius/spinning_cursor。用法见test方法。

【讨论】:

    【解决方案2】:

    您必须使用另一种方法(如 Curses)来就地更新两行。

    ablogaboutcode.com | web.archive.org

    ...和...

    http://www.ruby-doc.org/stdlib-1.9.3/libdoc/curses/rdoc/Curses.html

    【讨论】:

    • 博客文章的链接已失效。请用网络存档包装您的链接。
    • @Overbryd,它还没有死。但它的行为是不可预测的。
    【解决方案3】:

    git/progress.c

    ...
            eol = done ? done : "   \r";
    ...
                    fprintf(stderr, "...%s", ..., eol);
                    fflush(stderr);
    

    Git 只是简单地发出一个回车而不是换行,终端将其解释为“移动到第一列”。

    【讨论】:

      【解决方案4】:

      有许多用于 Ruby 的 curses 库。我相信 rbbcurse 是维护最多的。

      【讨论】:

        【解决方案5】:

        使用回车。 '\r' 通常应该可以工作。

        【讨论】:

        • 这是一个例子:10.times{|i| STDOUT.write "\r#{i}"; sleep 1}
        • 谢谢。但是为什么 \r 在 Unix 中会有这种效果呢?
        • 我需要添加 $stdout.flush 才能像这样在 Ubuntu 上工作:10.times { |i| $stdout.write "\r#{i}"; $stdout.flush; sleep 1 }
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-09-12
        • 1970-01-01
        • 2015-02-02
        • 1970-01-01
        • 2022-01-10
        相关资源
        最近更新 更多