Writing Over Previously Output Lines in the Command Prompt with Ruby

Writing over previously output lines in the command prompt with ruby

Use \r to move the cursor to the beginning of the line. And you should not be using puts as it adds \n, use print instead. Like this:

print "11MB 294K/s"
print "\r"
print "12MB 307K/s"

One thing to keep in mind though: \r doesn't delete anything, it just moves the cursor back, so you would need to pad the output with spaces to overwrite the previous output (in case it was longer).

By default when \n is printed to the standard output the buffer is flushed. Now you might need to use STDOUT.flush after print to make sure the text get printed right away.

Ruby windows command prompt previous lines

Yes you can, on a Windows Vista, 7 and probably 8 and also in some third-party extended command-interpreters like 4NT and Take Command you can recall previous commands by using the up-key, edit the line and re-execute the line. I don't see what Ruby has got to do with this. If you want to let Ruby type keystrokes in a console that is possible using the auto-it Active-X control.

EDIT: here a sample using Autoit to edit the console, downlaod and install it first and then run the following script. To make sure the script doesn't interact with other open consoles i copied mu cmd.exe to a cmd2.exe which is started up first.

require 'win32ole' 

title = "C:\\Windows\\System32\\cmd2.exe"
STDOUT.sync = true
ai = WIN32OLE.new("AutoItX3.Control")
ai.winwait(title)
ai.WinActivate(title, "")
ai.Send "cls{ENTER}"
1.upto(4) do |i|
ai.Send "line#{i}{ENTER}"
end
1.upto(4) do |i|
ai.Send "{UP}"
sleep 1
end
ai.Send "line one {ENTER}"

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

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.

How to overwrite a printed line in the shell with Ruby?

You can use the \r escape sequence at the end of the line (the next line will overwrite this line). Following your example:

require 'time'

loop do
time = Time.now.to_s + "\r"
print time
$stdout.flush
sleep 1
end

Ruby outputting to the same line as the previous output

Yes, use print var instead; puts automatically appends a new line, print doesn't.

How to overwrite the current console line after gets in Ruby?

Overwriting current line (printing "\r") works just fine with gets. The thing is, gets reads a line until (and including) a linebreak. So it is you, pressing ENTER, who moves cursor to a next line. And then this next, already empty, line is rewinded by \r.

Moving to a previous line is not possible in the regular mode. (see comments) You need to use a lower-level terminal window access. curses is a popular library. Ruby has bindings for it. I suggest you start with this blog post (and follow-ups to it): http://graysoftinc.com/terminal-tricks/random-access-terminal

How to code press key to continue

You can use STDIN from the IO class, rather than gets.

require 'io/console'                                                                                                       
def continue_story
print "press any key"
STDIN.getch
print " \r" # extra space to overwrite in case next sentence is short
end

puts "An awesome story begins..."
continue_story
puts "And ends after 2 lines"

This has the added bonus that it only requires one character to be entered (getch - get character) allowing the 'press any key' to work without a return or enter.

Deleting multiple lines of terminal output using ruby

Well, for simple things, if you can assume an ANSI-compatible terminal (usually a good bet), you can just directly output ANSI codes. For instance,

 5.times do |item|
print "\r" + ("\e[A\e[K"*3) if item > 0
puts "#{item+1}\nHello!"
end

Where \r moves the cursor to the start of the line, \e[A moves the cursor up one line, and \e[K clears from the cursor position to the end of the line. If you don't need anything further down the screen, you can also just send \e[J once you have the cursor where you want; that clears all the way to the end of the screen.

For more sophisticated stuff, you could start by taking a look at the Highline gem.

Persistent Command Line Display

Depending on your needs, erasing the screen and creating the grid again could be good enough :

def show_grid
line = '+---+---+---+---+---+---+---+---+---+'
puts line
9.times do
row = (1..9).map { rand(9) + 1 }
puts '| ' + row.join(' | ') + ' |'
puts line
end
end

def clear_screen
system('clear') || system('cls')
end

loop do
clear_screen
show_grid
sleep 1
end

It outputs

+---+---+---+---+---+---+---+---+---+
| 1 | 2 | 7 | 3 | 4 | 9 | 8 | 6 | 5 |
+---+---+---+---+---+---+---+---+---+
| 1 | 8 | 1 | 7 | 6 | 2 | 2 | 1 | 3 |
+---+---+---+---+---+---+---+---+---+
| 2 | 2 | 1 | 2 | 2 | 8 | 2 | 3 | 6 |
+---+---+---+---+---+---+---+---+---+
| 5 | 3 | 6 | 1 | 5 | 3 | 2 | 7 | 9 |
+---+---+---+---+---+---+---+---+---+
| 9 | 7 | 7 | 4 | 7 | 2 | 2 | 9 | 1 |
+---+---+---+---+---+---+---+---+---+
| 5 | 9 | 1 | 9 | 3 | 7 | 8 | 3 | 1 |
+---+---+---+---+---+---+---+---+---+
| 8 | 5 | 4 | 7 | 3 | 2 | 2 | 5 | 3 |
+---+---+---+---+---+---+---+---+---+
| 4 | 7 | 1 | 1 | 4 | 8 | 4 | 1 | 1 |
+---+---+---+---+---+---+---+---+---+
| 1 | 1 | 8 | 4 | 2 | 4 | 8 | 3 | 8 |
+---+---+---+---+---+---+---+---+---+

then

+---+---+---+---+---+---+---+---+---+
| 6 | 4 | 4 | 3 | 5 | 5 | 8 | 9 | 1 |
+---+---+---+---+---+---+---+---+---+
| 7 | 7 | 7 | 3 | 3 | 2 | 8 | 7 | 6 |
+---+---+---+---+---+---+---+---+---+
| 6 | 2 | 2 | 1 | 4 | 7 | 1 | 1 | 9 |
+---+---+---+---+---+---+---+---+---+
| 3 | 8 | 7 | 7 | 7 | 9 | 7 | 4 | 4 |
+---+---+---+---+---+---+---+---+---+
| 4 | 1 | 2 | 8 | 6 | 7 | 1 | 9 | 3 |
+---+---+---+---+---+---+---+---+---+
| 6 | 7 | 7 | 5 | 1 | 7 | 6 | 7 | 4 |
+---+---+---+---+---+---+---+---+---+
| 8 | 2 | 3 | 5 | 3 | 5 | 4 | 7 | 2 |
+---+---+---+---+---+---+---+---+---+
| 5 | 9 | 3 | 2 | 4 | 2 | 9 | 6 | 3 |
+---+---+---+---+---+---+---+---+---+
| 1 | 7 | 8 | 9 | 4 | 3 | 4 | 1 | 5 |
+---+---+---+---+---+---+---+---+---+

then...

It should work on Windows/Linux/MacOs.

For anything more complex, you'll need an ncurses gem.

Pre-Filled Prompt in Ruby

You can do it with RbReadline:

require 'rubygems'
require 'rb-readline'

module RbReadline
def self.prefill_prompt(str)
@rl_prefill = str
@rl_startup_hook = :rl_prefill_hook
end

def self.rl_prefill_hook
rl_insert_text @rl_prefill if @rl_prefill
@rl_startup_hook = nil
end
end

RbReadline.prefill_prompt("Previous query")
str = Readline.readline("Enter query: ", true)

puts "You entered: #{str}"


Related Topics



Leave a reply



Submit