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"
精彩评论