2010-02-25 5 views
29

Lorsque vous exécutez git clone, la progression est mise à jour. Par exemple, le pourcentage des objets reçus change en place.Sortie de progression sur place dans le terminal ou la console

[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 

Comment est-ce que ceci est accompli? Utilise-t-il des ncurses ou quelque chose d'encore plus simple, comme une combinaison de caractères de retour arrière et de sortie de caractères normaux?

Je suis particulièrement intéressé par la façon dont ce type de sortie de console peut être réalisé à partir de Ruby.

EDIT

Ma question originale réponse. Mais voici un addendum. Lorsque vous utilisez MPlayer, par exemple, il met non seulement à jour une ligne pour afficher la progression en cours, mais aussi la ligne précédente (par exemple lorsque vous appuyez sur pause).

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

Comment mettriez-vous à jour deux lignes de sortie sur place?

+1

Jetez également un coup d'œil à http://stackoverflow.com/questions/613305/infinite-yields-from-an-iterator – vladr

+0

Cette question et la réponse associée sont exactement ce qui rend le débordement de Stack génial. Merci pour eux. – num1

Répondre

36

Utilisez le retour chariot. '\ r' devrait normalement fonctionner.

+9

Voici un exemple: '10.times {| i | STDOUT.write "\ r # {i}"; sommeil 1} ' –

+0

Merci. Mais pourquoi avoir cet effet sous Unix? – dan

+1

@dan: Peut-être que cela a la réponse: http://en.wikipedia.org/wiki/Carriage_return –

0

Il existe un certain nombre de librairies de curses pour Ruby. Je crois que rbbcurse est le plus maintenu.

7

git/progress.c

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

Git émet simplement un retour chariot et aucun saut de ligne, auquel le terminal interprète comme « déplacer à la première colonne ».

4

Vous devrez utiliser une autre méthode (telle que Curses) pour mettre à jour deux lignes sur place.

ablogaboutcode.com | web.archive.org

... et ...

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

+0

Le lien vers l'article de blog est mort. S'il vous plaît envelopper vos liens avec les archives web. – Overbryd

+1

@Overbryd, ce n'est pas mort. Mais son comportement n'est pas prévisible. –

+0

Cool, merci beaucoup. Vraiment besoin de cet article. – Overbryd

1

j'ai écrit peu classe pour la mise à jour de sortie multiligne:

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 

Inspirée par prydonius/spinning_cursor. Voir test méthode pour l'utilisation de l'exemple.

Questions connexes