开发者

How to override or edit the last printed lines in a ruby CLI script?

I am trying to build a script that gives me feedback about progress on the command-line. Actually it is just putting a newline for every n-th progress step made. Console looks like

10:30:00 Parsed 0 of 1'000'000 data entries (0 %)
10:30:10 Parsed 1'000 of 1'000'000 data entries (1 %)
10:30:20 Parsed 2'000 of 1'000'000 data entries (2 %)
[...] etc [...]
11:00:00 Parsed 1'000'000 of 1'000'000 data entries (100 %)

Even if timestamp and progressnumbers are fictional,开发者_开发知识库 you should see the problem.

What I want is to do it "wget-style" with a progressbar updated on the command line, with linewidth in mind.

First I thought about the use of curses because I had hands on as I tried to learn C, but I never could get warm with it, also I think it is bloated for the purpose of manipulating just a few lines. Also I dont need any coloring. Also most other libraries I found seemed to be specialized for coloring.

Can someone help me with this problem?


A while ago I created a class to be a status text on which you can change part of the content of the text within the line. It might be useful to you.

The class with an example use are:

class StatusText
  def initialize(parms={})
    @previous_size = 0
    @stream = parms[:stream]==nil ? $stdout : parms[:stream]
    @parms = parms
    @parms[:verbose] = true if parms[:verbose] == nil
    @header = []
    @onChange = nil
    pushHeader(@parms[:base]) if @parms[:base]
  end

  def setText(complement)
    text = "#{@header.join(" ")}#{@parms[:before]}#{complement}#{@parms[:after]}"
    printText(text)
  end

  def cleanAll
    printText("")
  end

  def cleanContent
    printText "#{@parms[:base]}"
  end

  def nextLine(text=nil)
    if @parms[:verbose]
      @previous_size = 0
      @stream.print "\n"
    end
    if text!=nil
      line(text)
    end
  end

  def line(text)
    printText(text)
    nextLine
  end

  #Callback in the case the status text changes
  #might be useful to log the status changes
  #The callback function receives the new text
  def onChange(&block)
    @on_change = block
  end

  def pushHeader(head)
    @header.push(head)  
  end

  def popHeader
    @header.pop  
  end

  def setParm(parm, value)
    @parms[parm] = value
    if parm == :base
      @header.last = value
    end
  end

  private
  def printText(text)
    #If not verbose leave without printing
    if @parms[:verbose]
      if @previous_size > 0
        #go back
        @stream.print "\033[#{@previous_size}D"
        #clean
        @stream.print(" " * @previous_size) 
        #go back again
        @stream.print "\033[#{@previous_size}D"
      end
      #print
      @stream.print text
      @stream.flush
      #store size
      @previous_size = text.gsub(/\e\[\d+m/,"").size
    end
    #Call callback if existent
    @on_change.call(text) if @on_change
  end
end

a = StatusText.new(:before => "Evolution (", :after => ")")

(1..100).each {|i| a.setText(i.to_s); sleep(1)}
a.nextLine

Just copy, paste in a ruby file and try it out. I use escape sequences to reposition the cursor.

The class has lots of features I needed at the time (like piling up elements in the status bar) that you can use to complement your solution, or you can just clean it up to its core.

I hope it helps.


In the meanwhile I found some gems that give me a progressbar, I will list them up here:

  • ProgressBar from paul at github
    • a more recent version from pgericson at github
  • ruby-progressbar from jfelchner at github
  • simple_progressbar from bitboxer at github

I tried the one from pgericson and that from jfelchner, they both have pros and cons but also both fits my needs. Probably I will fork and extend one of them in the future.

I hope this one helps others to find faster, what I searched for months.


Perhaps replace your outputting to this:

print "Progress #{progress_var}%\r"
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜