2010-02-25 12 views
29

Cuando ejecuta git clone, actualiza el progreso en su lugar. Por ejemplo, el porcentaje de los objetos recibidos cambia en su lugar.Salida de progreso en el lugar en el terminal o consola

[email protected]:~/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 

¿Cómo se logra esto? ¿Utiliza ncurses o algo aún más simple, como alguna combinación de caracteres de retroceso y salida de caracteres regulares?

Estoy especialmente interesado en cómo este tipo de salida de consola podría lograrse desde Ruby.

EDITAR

se respondió a mi pregunta original. Pero aquí hay un apéndice. Cuando utiliza MPlayer, por ejemplo, no solo actualiza una línea para mostrar el progreso actual, sino también la línea anterior (por ejemplo, cuando presiona pausa).

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

¿Cómo actualizarías dos líneas de salida en el lugar?

+1

También eche un vistazo a http://stackoverflow.com/questions/613305/infinite-yields-from-an-iterator – vladr

+0

Esta pregunta y la respuesta asociada son exactamente lo que hace que el desbordamiento de pila sea excelente. Gracias por ellos – num1

Respuesta

36

Utilizar retorno de carro. '\ r' generalmente debería funcionar.

+9

Aquí hay un ejemplo: '10.times {| i | STDOUT.write "\ r # {i}"; dormir 1} ' –

+0

Gracias. ¿Pero por qué tiene ese efecto en Unix? – dan

+1

@dan: Quizás esto tiene la respuesta: http://en.wikipedia.org/wiki/Carriage_return –

0

Hay una serie de librbaries de maldiciones para Ruby. Creo que rbbcurse es el más mantenido.

7

git/progress.c

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

Git simplemente emite un retorno de carro y no de avance de línea, que el terminal interpreta como "mover a la primera columna".

1

escribí poco de clase para la actualización de varias líneas de salida:

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 [email protected]_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 

Inspirado por prydonius/spinning_cursor. Ver el método test por ejemplo uso.

Cuestiones relacionadas