I am looking for a way to take input from a terminal in a function called from the main loop of a program.
I am not worried about it executing scripts halfway through writing them as the main loop is paused when the application window (not the terminal or console window) loses focus.
This is dev-only so any mishaps with eval lie with me
Edit: I forgot to say, I want the main loop to continue uninterrupted if there is no input.
In Ruby, Kernel#gets will return nil when there is no more line to read (i.e. EOF is reached).
So the following script would do the equivalent to the above Java code:
script = ''
while line = gets
script += line
end
You can capture the input lines as an array and join the elements by newline:
script = STDIN.readlines.join("\n")
To evaluate it:
eval script rescue nil
Or:
begin
eval script
rescue
end
As you noted in the comments, you can STDOUT.flush if you need to. If the Ruby program is ending immediately, there's no need to flush stdout.
This works but only because self.reload! is not called while my console window is selected
Thread.new do
loop do
script_temp = gets
#script = #script + script_temp
end
end
def self.reload!
begin
eval #script
$stdout.flush()
rescue Exception
end
#script = ''
end
Based on this
Related
So following this SO guidance, I wrote a program such as:
ruby_cli.rb
while true
input = [(print 'Name: '), gets.rstrip][1]
input.downcase.strip
end
What I want to happen is when I press CTRL+D (which as I understand is EOF) at the command prompt, the ruby program breaks out of the while loop and ends. Right now it hits the input.downcase.strip line and I get an error such as undefined method `downcase' for nil:NilClass (NoMethodError)
How would you accomplish this?
Edit:
The reason I am using input = (print 'Name: '), gets.rstrip is because I want to print "Name" as the prompt before every user input I ask for.
The following should do the trick for you:
loop do # infinite loop
print 'Name: ' # prompt for input
response = gets # get the response -- gets returns nil on EOF
break unless response # break out of the loop unless the response is non-nil
p response.rstrip.downcase # do whatever you want with the response
end
EOF is ctrl-d for Unix/Mac, ctrl-Z for Windows.
Try checking gets using a while loop, like this:
print 'Name: '
while line = gets
# operate on line
print 'Name: '
end
I am pretty sure that all of you know the bash pipelines. What I am trying to do is to read the output from one ruby script and put it as input into other ruby script
here is what I successfully accomplished so far:
first file to generate the output
#one.rb
puts "hello"
and the second file to handle the input
#two.rb
msg = gets.chomp
IO.write(File.dirname(__FILE__)+'\out.txt',msg)
now, the cygwin command (or linux) (side question: can this be done as well in windows cmd or powershell?)
ruby one.rb | ruby two.rb
and voila, the out.txt file is created, filled with string hello.
However, when I try it to do it in the cycle or handling some stream of data, like 10.times puts 'hello' and the in cycle to read it, it did not work. Can somebody please help me to accomplish this or show me the way how can i do that? I have only find some python question but it was for something different and some bash-like-way.
Thank you!
Instead of reading just one line (msg = gets.chomp) you need to iterate through the lines of stdin
$stdin.each_line do |msg|
# ...
end
This does not wait for the entire output to be generated, it will process the stream as lines are printed by the first process (ignoring buffering).
For example, with these two scripts
# one.rb
i = 0
loop { puts i += 1 }
# two.rb
$stdin.each_line { |msg| puts "#{msg.chomp}!" }
The first has infinite output, but you will still see output when you run
ruby one.rb | ruby two.rb
You need to wait until STDIN sends an EOF in the second script.
Try:
#two.rb
msg = ""
while line = gets
msg += line.chomp
end
IO.write(File.dirname(__FILE__)+'\out.txt',msg)
If the fist script contains 10.times { puts "hello" } you'll get:
hellohellohellohellohellohellohellohellohellohello
To read all the input, in two.rb change
msg = gets.chomp
to
msg = $stdin.read.chomp
If you want to read line-by-line, add some command line flags:
ruby -n two.rb
and two.rb becomes
BEGIN {out = File.open(File.dirname(__FILE__)+"/out.txt", "w")}
out.write $_
Using / instead of \ is OK on Windows.
Basically in my search for code which will loop, and terminate upon user input, i managed to find code here, and after some alteration, produced this:
#desired destination method, however loop persists!!
def desired_method
print "method entered"
end
Thread.new do
while line = STDIN.gets
break if line.chomp == "" # code detects user input
end
desired_method
end
# program will loop here until user presses enter
loop do
puts "foo"
sleep 1
end
This code is brilliant, and will enter the method 'desired_method' when i hit enter, however the loop persists!! printing 'foo' perpetually after "method entered"!!. I have done some research prior to posting this question on how to kill threads, which i believe may hold the answer. My attempts included naming the thread and using the 'thread.exit' function to kill it, however these techniques have remained unsuccessful.
Can anyone illustrate how i might enter the 'desired_method' method without the persisting "foo" print?
Thanks in advance, and greatly appreciated.
An easy solution here is to use semaphore, signalling between threads with a variable access to both places:
# This will be out stop flag, for signalling between threads.
#time_to_stop = false
def desired_method
print "method entered"
# Here we want the loop in the other thread to stop.
#time_to_stop = true
end
Thread.new do
while line = STDIN.gets
break if line.chomp == "" # code detects user input
end
desired_method
end
# program will loop here until user presses enter
loop do
puts "foo"
sleep 1
# only continue if the stop flag is not set.
break if #time_to_stop
end
Hope this helps.
I'm trying to find a way to terminate a loop when the user hits 'x'+Enter. I want the loop to just keep running in the background until the user cancels it.
Something along these lines:
while gets.chomp != 'x'
puts 'looping...'
sleep 1
end
I'm a beginner with programming and have searched far and wide for how to do this so any help would be deeply appreciated.
You have to use threads for this:
Thread.new do
while line = STDIN.gets
break if line.chomp == 'x'
end
exit
end
# whatever you want to do in the background
# (or rather in the foreground, actually)
loop do
puts "foo"
sleep 1
end
The problem is that STDIN.gets blocks, so you can't do something else at the same time without parallelizing the program by using a background thread that only checks for input.
I have a while loop consistently listening to incoming connections and outputting them to console. I would like to be able to issue commands via the console without affecting the output. I've tried:
Thread.new do
while true
input = gets.chomp
puts "So I herd u sed, \"#{input}\"."
#Commands would be in this scope
end
end
However, that seems to pause my entire script until input is received; and even then, some threads I have initiated before this one don't seem to execute. I've tried looking at TCPSocket's select() method to no avail.
Not sure where are the commands you want to "continue running" in your example. Try this small script:
Thread.new do
loop do
s = gets.chomp
puts "You entered #{s}"
exit if s == 'end'
end
end
i = 0
loop do
puts "And the script is still running (#{i})..."
i += 1
sleep 1
end
Reading from STDIN is done in a separate thread, while the main script continues to work.
Ruby uses green threads, so blocking system calls will block all threads anyway. An idea:
require 'io/wait'
while true
if $stdin.ready?
line = $stdin.readline.strip
p "line from stdin: #{line}"
end
p "really, I am working here"
sleep 0.1
end