I am looking to have a ruby program (a rake task) observe an output from another rake task. The output writer outputs to stderr. I'd like to read those lines. I'm having difficulty setting it up. If I have a writer (stdout_writer.rb) that constantly prints something:
#!/usr/bin/env ruby
puts 'writing...'
while true
$stdout.puts '~'
sleep 1
end
and a file that reads it and echoes (stdin_reader.rb):
#!/usr/bin/env ruby
puts 'reading...'
while input = ARGF.gets
puts input
input.each_line do |line|
begin
$stdout.puts "got line #{line}"
rescue Errno::EPIPE
exit(74)
end
end
end
and I'm trying to have them work together, nothing happens:
$ ./stdout_writer.rb 2>&1 | ./stdin_reader.rb
$ ./stdout_writer.rb | ./stdin_reader.rb
nothing... although if I just echo into stdin_reader.rb, I get what I expect:
piousbox#e7440:~/projects/opera_events/sendgrid-example-operaevent$ echo "ok true" | ./stdin_reader.rb
reading...
ok true
got line ok true
piousbox#e7440:~/projects/opera_events/sendgrid-example-operaevent$
so how would I setup a script that gets stderr piped into it, so that it can read it line-by-line? Additional info: this will be an ubuntu upstart service script1.rb | script2.rb where script1 sends a message, and script2 verifies that the message was sent by script1
The issue seems to be that as stdout_writer runs infinitely, stdin_reader will never get a chance to read the STDOUT from stdout_writer as the pipe, in this case, is waiting for stdout_writer to be finished before stdin_reader starts reading. I tested this by changing while true to 5.times do. If you do that, and wait 5 seconds, the result of ./stdout_writer.rb | ./stdin_reader.rb is
reading...
writing...
got line writing...
~
got line ~
~
got line ~
~
got line ~
~
got line ~
~
got line ~
This isn't an issue with your code itself, but more so an issue with the way that ruby execution in terms of STDOUT | STDIN handling works.
Also, I don't think I've ever learned as much as I learned researching this question. Thank you for the fun exercise.
The output from stdout_writer.rb is being buffered by Ruby, so the reader process doesn’t see it. If you wait long enough, you should see the result appear in chunks.
You can turn buffering off and get the result you’re expecting by setting sync to true on $stdout at the start of stdout_writer.rb:
$stdout.sync = true
Related
How to write expect script which executes command and prints just the command's output?
I've tried various things but none works, e.g.
#!/usr/bin/expect
log_user 0
spawn bash
send "echo 1\r"
log_user 1
expect "1"
log_user 0
send "exit\r"
expect eof
Gives in output:
echo 1
While I need just "1" . I hope somebody knows simple solution how to fix my example
Capturing the output from sent commands is a bit of a pain in expect.
Here's a more general case that does not rely on the log_user setting, it captures the output with a regular expression:
#!/usr/bin/expect
log_user 0
spawn bash
# set the prompt to a known value
send "PS1='>'\r"
expect -re {>$}
# send a command: we don't know what the output is going to be
send "echo \$RANDOM\r"
# capture the portion of the output that occurs just before the prompt
expect -re "\r\n(.*?)\r\n>$"
puts "output is: $expect_out(1,string)"
send "exit\r"
expect eof
A thought just occurred to me: if the command does not require any interaction, then expect is overkill: just use exec
set output [exec bash -c {echo $RANDOM}]
Ok, it looks following script does (at least similar to) what I need:
log_user 0
spawn bash
expect "#" {} "\\\$" {}
send -- "echo AA\r"
expect -- "echo AA\r" {}
log_user 1
expect -- "AA"
log_user 0
send -- "exit\r"
expect eof
Hi I am trying to store the output of a command run through a spawn ssh remote window into my local host, I am new to expect and am not able to figure out where I am wrong.
My Code:
#!/bin/bash
while read line
do
/usr/bin/expect <<EOD
spawn ssh mininet#$line
expect "assword:"
send -- "mininet\r"
set output [open "outputfile.txt" "a+"]
expect "mininet#mininet-vm:*"
send -- "ls\r"
set outcome $expect_out(buffer)
send "\r"
puts $output "$outcome"
close $output
expect "mininet#mininet-vm:*"
send -- "exit\r"
interact
expect eof
EOD
done <read_ip.txt
I am getting the error
expect: spawn id exp6 not open
while executing
"expect "mininet#mininet-vm:*""
Please can any body help me on this code.
You have your expect program in a shell heredoc. The shell will expand variables in the heredoc before launching expect. You have to protect expect's variables from the shell.
One way is to use a 'quoted' heredoc, and pass the shell variable to expect through the environment:
#!/bin/bash
export host ## an environment variable
while read host
do
/usr/bin/expect <<'EOD' ## note the quotes here
spawn ssh mininet#$env(host) ## get the value from the environment
expect "assword:"
send -- "mininet\r"
set output [open "outputfile.txt" "a+"]
expect "mininet#mininet-vm:*"
send -- "ls\r"
set outcome $expect_out(buffer)
send "\r"
puts $output "$outcome"
close $output
expect "mininet#mininet-vm:*"
send -- "exit\r"
expect eof ## don't want both "interact" and "expect eof"
EOD
done <read_ip.txt
Putting single quotes around the heredoc terminator means the whole heredoc acts like a single quoted string, and expect's variables are left for expect to handle.
You might also investigate the expect log_file command: you can enable and disable logging at will, much as you are doing manually here.
I start a script simple.rb with ruby simple.rb > log.txt &. I want it to run infinitely. It runs for a while, but pidof ruby does not return anything. The script stops running, and there is no error code or exit msg in the log file. What happened? Do ruby loops end eventually? I can restart the ruby script when it ends from a bash endless loop, but I'm curious as to why this script ends, and how I can find out if it doesn't emit an exit code/msg.
def main_loop
puts "Doing stuff.."
end
while true
main_loop
sleep 5.seconds
end
The #seconds is unnecessary and is probably messing up your code, since #sleep takes a number (float or integer, I believe). See http://apidock.com/ruby/Kernel/sleep .
$stdout.sync = true
def main_loop
puts "Doing stuff.."
end
while true
main_loop
sleep 5
end
I am trying to print process status on webpage. But when execute host:port/status method I dont see any response. It returns a blank page. ps -ef command executes on command line. I tried printing it on getStatus Method but it doesnt print it.
I want to display process execution status on website.
def getStatus
puts #{system('ps -ef | grep abc.jar|grep -v grep')? "Running": "Stopped"}
return #{system('ps -ef | grep abc.jar|grep -v grep')? "Running": "Stopped"}
end
get '/status' do
return getStatus
end
The expression
puts #{…
will only print a newline character, since # outside a string introduces a comment, same with return #….
To get the actual output, use something like this (I took the freedom to transformed your code snippet into more idiomatic Ruby):
def running?
`ps -ef` =~ /abc\.jar/
end
get '/status' do
status = running? ? 'Running' : 'Stopped'
logger.debug "Status: #{status}"
status
end
Now the running? method performs your check:
get the result of ps -ef via Kernel#`
match that result against the regular expression /abc\.jar/ via String#=~ (basically perform grep abc\.jar in Ruby land)
Step 1 is performed in a sub shell and everything in the sub shell is returned into Ruby land, whereas Kernel#system will only return whether the command exited with a non-zero exit status. Any output from commands started with system('...') is also redirected to stdout Your inital code snippet would not have worked that way, since grep -v grep will always exit with status 0.
(technically, a sub shell is not required, but the IO.popen call is more complex)
I'm trying to setup a simple timer that gets started from a Rails Application. This timer should wait out its duration and then start a shell script that will start up ./script/runner and complete the initial request. I need script/runner because I need access to ActiveRecord.
Here's my test lines in Rails
output = `at #{(Time.now + 60).strftime("%H:%M")} < #{Rails.root}/lib/parking_timer.sh STRING_VARIABLE`
return render :text => output
Then my parking_timer.sh looks like this
#!/bin/sh
~/PATH_TO_APP/script/runner -e development ~/PATH_TO_APP/lib/ParkingTimer.rb $1
echo "All Done"
Finally, ParkingTimer.rb reads the passed variable with
ARGV.each do|a|
puts "Argument: #{a}"
end
The problem is that the Unix command "at" doesn't seem to like variables and only wants to deal with filenames. I either get one of two errors depending on how I position "s
If I put quotes around the right hand side like so
... "~/PATH_TO_APP/lib/parking_timer.sh STRING_VARIABLE"
I get,
-bash: ~/PATH_TO_APP/lib/parking_timer.sh STRING_VARIABLE: No such file or directory
I I leave the quotes out, I get,
at: garbled time
This is all happening on a Mac OS 10.6 box running Rails 2.3 & Ruby 1.8.6
I've already messed around w/ BackgrounDrb, and decided its a total PITA. I need to be able to cancel the job at any time before it is due.
After playing around with irb a bit, here's what I found.
The backtick operator invokes the shell after ruby has done any interpretation necessary. For my test case, the strace output looked something like this:
execve("/bin/sh", ["sh", "-c", "echo at 12:57 < /etc/fstab"], [/* 67 vars */]) = 0
Since we know what it's doing, let's take a look at how your command will be executed:
/bin/sh -c "at 12:57 < RAILS_ROOT/lib/parking_timer.sh STRING_VARIABLE"
That looks very odd. Do you really want to pipe parking_timer.sh, the script, as input into the at command?
What you probably ultimately want is something like this:
/bin/sh -c "RAILS_ROOT/lib/parking_timer.sh STRING_VARIABLE | at 12:57"
Thus, the output of the parking_timer.sh command will become the input to the at command.
So, try the following:
output = `#{Rails.root}/lib/parking_timer.sh STRING_VARIABLE | at #{(Time.now + 60).strftime("%H:%M")}`
return render :text => output
You can always use strace or truss to see what's happening. For example:
strace -o strace.out -f -ff -p $IRB_PID
Then grep '^exec' strace.out* to see where the command is being executed.