终端或控制台中的原地进度输出

35
当你运行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时,它不仅更新一行以显示当前进度,还会更新上一行(例如当你按下暂停键)。
 =====  PAUSE  =====
A:  79.9 (01:19.9) of 4718.0 ( 1:18:38.0)  0.3% 

如何原地更新两行输出?


1
请参阅https://dev59.com/DkbRa4cB1Zd3GeqP3sYO - vladr
这个问题和相关的答案正是使得Stack Overflow如此出色的原因。非常感谢你们。 - num1
5个回答

40

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


11
这是一个例子:10.times{|i| STDOUT.write "\r#{i}"; sleep 1} - Mladen Jablanović
谢谢。但在Unix中\r为什么会产生这种效果? - dan
1
我需要添加 $stdout.flush 以使其在 Ubuntu 上正常工作,如下所示:10.times { |i| $stdout.write "\r#{i}"; $stdout.flush; sleep 1 } - user266647

8

git/progress.c

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

Git只是发出回车符而没有换行符,终端会将其解释为“移到第一列”。

3

博客文章的链接已失效。请使用网络档案包装您的链接。 - Overbryd
1
@Overbryd,它并没有死。但是它的行为是不可预测的。 - Ivan Black

3

我写了一个用于多行输出更新的小类:

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 方法以了解使用示例。


-1

有许多用于Ruby的curses库。我相信rbbcurse是最受关注的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接