I found out that you can't share variables in any way (not even $global ones)and that you must use pipes, so I will cut to the chase.
There are two processes: Process A gets input from somewhere (possibly pipe in the command line) and sends it to Process (fork) B which will print the last given value every second.
This is basically a simple, basic version of what I need. I tried to do it,but it doesn't show any output. I can't figure out why.
This is the source code:
#!/usr/bin/ruby
# create two pipes.
reader, writer = IO.pipe
fork do
loop do
# print the last received value every second (or so)
puts "Last name is: #{reader.read }"
sleep 1
end
end
fork do
ARGF.each_line do |e|
name = e.chomp
#send the data through the pipe
writer.write name
end
#make sure all forks are killed
Process.waitall
In order to get the desired input here is a sample script which you can pipe:
#!/usr/bin/ruby
arr = ['Sally', 'Mike', 'John', 'Steve', 'Iana', 'That guy']
loop do
puts arr[rand(5)]
sleep 1
STDOUT.flush
end
and simply pipe it:
./generate_input.rb | ./program.rb
IO#read with no length parameter reads until EOF. Since you're not sending EOF (that is, you're not closing the writer), it never returns. You might try something like reader.gets and writer.puts to write data one line at a time.
Something like this will work as expected:
reader, writer = IO.pipe
fork do
loop do
# print the last received value every second (or so)
puts "Last name is: #{reader.gets }"
sleep 1
end
end
fork do
ARGF.each_line do |e|
name = e.chomp
#send the data through the pipe
writer.puts name
end
end
#make sure all forks are killed
Process.waitall
Related
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 finding this a bit odd. I would expect each iteration of parse_line(ARGF.read_line) here to wait for input from STDIN. What actually is happening is the first iteration skips waiting for user input, but subsequent iterations do wait. My temporary workaround is just to iterate an extra time.
def parse_line(line)
line.split(" ").map(&:to_i)
end
def get_number_of_slices
parse_line(ARGF.readline(1)).first
end
def get_all_slice_dimensions(number_of_slices)
number_of_slices += 1 # for some reason the first read doesn't wait for user input...
number_of_slices.times.collect { puts "iter"; parse_line(ARGF.readline) }
end
def main
puts (get_all_slice_dimensions get_number_of_slices)
end
main
Added code so that it is a full working program. I found that when I changed readline(1) to readline in get_number_of_slices, I did not need to add 1 to number_of_slices in get_all_slice_dimensions any longer, but I would still like to know the reason why this affects the behavior. If you pay attention to the output, iter will print twice the first time.
I wonder how can I interact with never-ending(eternal looping) child process.
source code of loop_puts.rb, child process :
loop do
str = gets
puts str.upcase
end
main.rb :
Process.spawn("ruby loop_puts.rb",{:out=>$stdout, :in=>$stdin})
I want to put some letter, not by my hand typing, and get result(not previous result) in variable.
how can I do this?
thanks
There are a number of ways to do this and it's hard to recommend one without more context.
Here's one way using a forked process and a pipe:
# When given '-' as the first param, IO#popen forks a new ruby interpreter.
# Both parent and child processes continue after the return to the #popen
# call which returns an IO object to the parent process and nil to the child.
pipe = IO.popen('-', 'w+')
if pipe
# in the parent process
%w(please upcase these words).each do |s|
STDERR.puts "sending: #{s}"
pipe.puts s # pipe communicates with the child process
STDERR.puts "received: #{pipe.gets}"
end
pipe.puts '!quit' # a custom signal to end the child process
else
# in the child process
until (str = gets.chomp) == '!quit'
# std in/out here are connected to the parent's pipe
puts str.upcase
end
end
Some documentation for IO#popen here. Note that this may not work on all platforms.
Other possible ways to approach this include Named Pipes, drb, and message queues.
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