Why long output gets truncated when using popen in ruby? - ruby

I run a script that returns a long JSON on stdout, but apparently it is truncated:
res = ''
Open3.popen2('node script.js') {|i,o,t|
while line = o.gets
res = res + line
end
}
puts res # it is truncated
Why? How can I avoid it?

You may be running into a known issue with node piping its standard output to another process. This is only an educated guess because I don't know what your node script is doing.
The issue:
If you're trying to write to stdout using fs.open+fs.write or fs.openSync+fs.writeSync, the output may be incomplete, so you may need to add extra logic to write the remainder.
Based on this nodejs github comment, the recommended way to write a buffer fully to stdout is
process.stdout.write(chunk[, encoding][, callback])
See the Stream API for details on this function. The github link above has more explanation about the issue with using fs.write/fs.writeSync.
I have tested process.stdout.write - to a piped stdout. Even a 16 MB string was written completely.

Related

How to read a file from command line using < operator and read user input afterwards?

I am writing a program in which I am taking in a csv file via the < operator on the command line. After I read in the file I would also like to ask the user questions and have them input their response via the command line. However, whenever I ask for user input, my program skips right over it.
When I searched stack overflow I found what seems to be the python version here, but it doesn't really help me since the methods are obviously different.
I read my file using $stdin.read. And I have tried to use regular gets, STDIN.gets, and $stdin.gets. However, the program always skips over them.
Sample input ruby ./bin/kata < items.csv
Current File
require 'csv'
n = $stdin.read
arr = CSV.parse(n)
input = ''
while true
puts "What is your choice: "
input = $stdin.gets.to_i
if input.zero?
break
end
end
My expected result is to have What is your choice: display in the command and wait for user input. However, I am getting that phrase displayed over and over in an infinite loop. Any help would be appreciated!
You can't read both file and user input from stdin. You must choose. But since you want both, how about this:
Instead of piping the file content to stdin, pass just the filename to your script. The script will then open and read the file. And stdin will be available for interaction with the user (through $stdin or STDIN).
Here is a minor modification of your script:
arr = CSV.parse(ARGF) # the important part.
input = ''
while true
puts "What is your choice: "
input = STDIN.gets.to_i
if input.zero?
break
end
end
And you can call it like this:
ruby ./bin/kata items.csv
You can read more about ARGF in the documentation: https://ruby-doc.org/core-2.6/ARGF.html
This has nothing to do with Ruby. It is a feature of the shell.
A file descriptor is connected to exactly one file at any one time. The file descriptor 0 (standard input) can be connected to a file or it can be connected to the terminal. It can't be connected to both.
So, therefore, what you want is simply not possible. And it is not just not possible in Ruby, it is fundamentally impossible by the very nature of how shell redirection works.
If you want to change this, there is nothing you can do in your program or in Ruby. You need to modify how your shell works.

How to execute a Ruby code from inside a Ruby script?

I'm trying to execute a Ruby script file.
Assuming the input is a string that contains the file content.
What are the possible ways? taking into considerations that I need to keep the output of the executed file whether stdout or not separated from the Main script.
As an example of what I'm trying to do is have a function called execute(code)
Then calling execute('4 + 5') would return 9 although I can write a whole Ruby script in the place of '4 + 5'.
If anyone can forward me to any related tutorials or books, I'd be thankful :)
You can call shell commands in Ruby, it's as simple and intuitive as surrounding your desired command in backticks.
The output gets returned, so just save it to a variable:
script1.rb:
puts "asdf"
script2.rb:
output = `ruby script1.rb`
puts output
"asdf"
I question what exactly it is you're trying to do, though. Because this is totally unintuitive and roundabout. Are you sure you aren't just looking for a module or something?

Ruby : get output of external command even when there is no line break

I try to run an external command in Ruby, and parse its output .
IO.popen(command, :err=>[:child, :out]) {|ls_io|
ls_io.each do |line|
print line
end
}
This way of doing it works wonders… except when I parse the progress-output of a c-program that shows it progress to stdout with \r.
As long as the c-program has not outputted a \n (that is as long as it has not finished some long-operation), Ruby waits and sees nothing. Then when a \n is outputted, Ruby sees it all
1%\r2%\r3%\r…100%
task finished
I tried all of the many ways to call external commands (eg Calling shell commands from Ruby) ; but none seem to capture the progress. I also tried every opeartor such as STDOUT.sync = true, and the c-program does call fflush(stdout)
I finally found a workaroud. I do :
IO.popen(commande, :err=>[:child, :out]) {|ls_io|
while true
byte=ls_io.read(1)
if byte.nil?
break
end
print byte
end
}
It's stupid… but it works.
Any more elegant way, and much more efficient way to do this ? Performance is terrible, as if the "refresh rate" was slow.
Set the input record separator to "\r" right before your block (provided you know it in advance):
$/ = "\r"
Reference of global preset variables: http://www.zenspider.com/Languages/Ruby/QuickRef.html#pre-defined-variables

Evolution e-mail client, pipe to program, code always returns 0

I am using "pipe to program" option in Evolution email client, that runs following ruby script
#!/usr/bin/ruby
%% example code below
junk_mail = 2
junk_mail
Now this program always returns 0, irrespective of what the value of junk_mail variable is.
I guess it has something to do with Evolution forking a child process to run this code, and always 0 (clean exit) is received back?
Help needed.
EDIT
I figured out actual problem is with data being read from pipe. Following code works fine when tested in command line, but it is unable to read pipe data when called from Evolution client
#!/usr/bin/ruby
email_txt = ARGF.read
File.open("~/debug.txt", 'a') { |file| file.write(email_txt + "\n") }
$cat email.txt | ./myprog.rb
This gives debug.txt as expected, but when called from Evolution pipe-to-program, it gives empty data.
Am I using the correct way to read piped stream data when called from external program? (I am under Fedora 20).
Use exit:
#!/usr/bin/ruby
junk_mail = 2
exit junk_mail
You can test this by running it from the command line in linux, then echoing the exit value via
echo $?
EDIT
To read STDIN into a single string:
email_txt = STDIN.readlines.join

Calling a Perl script from Ruby

I am currently attempting to figure out a way to call a Perl script from Ruby and have it output as if I was in the terminal and would allow me to provide input if it is needed.
I have figured out how I can do this and get the input after the fact but because the Perl script is still running, I am not able to run anything else.
I should note that I can not edit the Perl scripts. These scripts are being provided and this Ruby script is being made to make the process of running all of the Perl scripts easier and ensuring they are in the right order.
upgradestatus = `#{upgradearray[arraylocation]}`
This would be the relevant part my code for this. I have attempted a few other variations of how to do this but I get the same situation every time. When the script starts running it requires input so it just sits there.
You can't do what you want using backticks, %x or as a normal sub-shell, because they lack the ability to watch the output of the sub-command's output.
You could do it using Open3's popen2 or popen3 methods. They let you send to the STDIN stream for the called program, and receive data from the STDOUT. popen3 also lets you see/capture the STDOUT stream too. Unfortunately, often you have to send, then close the STDIN stream before the called program will return its information, which might be the case of the Perl scripts.
If you need more control, look into using Ruby's built-in Pty module. It's designed to let you talk to a running app through a scripting mechanism. You have to set up code to look for prompts, then respond to them by sending back the appropriate data. It can be simple, or it can be a major PITA, depending on the code you're talking to.
This is the example for the open command:
PTY.open {|m, s|
p m #=> #<IO:masterpty:/dev/pts/1>
p s #=> #<File:/dev/pts/1>
p s.path #=> "/dev/pts/1"
}
# Change the buffering type in factor command,
# assuming that factor uses stdio for stdout buffering.
# If IO.pipe is used instead of PTY.open,
# this code deadlocks because factor's stdout is fully buffered.
require 'io/console' # for IO#raw!
m, s = PTY.open
s.raw! # disable newline conversion.
r, w = IO.pipe
pid = spawn("factor", :in=>r, :out=>s)
r.close
s.close
w.puts "42"
p m.gets #=> "42: 2 3 7\n"
w.puts "144"
p m.gets #=> "144: 2 2 2 2 3 3\n"
w.close
# The result of read operation when pty slave is closed is platform
# dependent.
ret = begin
m.gets # FreeBSD returns nil.
rescue Errno::EIO # GNU/Linux raises EIO.
nil
end
p ret #=> nil

Resources