How to Overwrite a Printed Line in the Shell with Ruby

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

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

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.

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.

remove and replace the text when putting a string into the console $stdin ruby

If you are trying to implement a chat app in terminal, the "appropriate" tool is curses. A curses library essentially lets you write a GUI in the terminal. It lets you define separate regions of the screen that you can update separately and also lets you read input without echoing it to the terminal.

Overwrite last line on terminal

In your example you delete the text at the same line. When you want to return to the previous line use \e[1A, and to clear that line, use \e[K:

echo 'Old line'
echo -e '\e[1A\e[Knew line'

When you want to go N lines up, use \e[<N>A

How to read and overwrite text file simultaneously in Ruby


ARGV.each do |filename|
new_content = File.readlines(filename).uniq(&:rstrip).join.rstrip
File.write(filename, new_content)
end

This reads the entire file as individual lines, removes duplicate lines (ignoring the new line character(s)), concatenates the lines to create new file's content as one string and removes white spaces from its end. Finally we write new content to the file overriding existing content.

Note that the new version of the file won't have new line character at the end. If you want new line character at the end of the file you can just append it to new_content before the File::write call.


If you want to use the same handle for reading and writing, then do something like this:

ARGV.each do |filename|
File.open(filename, 'r+') do |f|
new_content = f.readlines.uniq(&:rstrip).join.rstrip # Read the entire file as individual lines, removes duplicate lines (ignoring the new line character(s)), concatenate the lines to create new file's content as one string and remove white spaces from its end
f.rewind # Rewind to the beginning of the file
f.write(new_content) # Write new content to the file overriding existing content
f.truncate(f.pos) # Remove the file's content after the current position
end
end

Although I can't see any real advantage of this version vs the first. The code becomes more messy, and we are forced to use File#truncate which is described as "Not available on all platforms.".

Override ENV['HOME'] for development of a Ruby command line app

The way I would do this would be to keep any development specific stuff out of the actual application code, but to have a Rakefile to use for testing during development. In there you could make sure the environment is set up appropriately, something like:

desc "Run the app"
task :exec do
ENV['HOME']= "somewhere else"
exec "./bin/your_binary"
end

You would then run rake exec (or give the task a better name) to run the development version, whilst still being able to run the real version. If you keep your development bin directory of your PATH there shouldn’t be any chance to mix up the two commands.

If you do want to be able to run the development version directly you could use the fact that when you run a gems binary the actual file that is executed is the wrapper file that Rubygems creates. You could check for this with the __FILE__ == $0 idiom in the top of your executable:

if __FILE == $0
# executing directly, probably in dev environment
ENV['HOME'] = "somewhere else"
end

When calling your file directly the environment will be replaced, when calling an installed gem $0 will be the wrapper file so the original environment will be used.

The “normal” way to do this is to just set the environment from your shell, e.g. in Bash:

$ HOME='somewhere else' ./bin/the_executable

The danger here of course is that you might forget to set the environment, causing you to trash some of you files. You could get round that by setting the new environment for your whole seesion:

$ export HOME='somewhere else'
$ ./bin/the_executable

but this will likely effect other tools that use HOME so it’s not advisable.

My advice would be to go for the Rakefile option, with the __FILE == $0 option as second choice.

Shell command for in-place multi-line regex replace

Using gets(nil) saves you a whopping 6 characters :)

ruby -i -e 'print gets(nil).sub(/<.*>/m, "d")' file.txt

From the gets docs:

The optional argument specifies the record separator. The separator is included with the contents of each record. A separator of nil reads the entire contents. [...]



Related Topics



Leave a reply



Submit