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

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

Related

Why is Ruby STDOUT buffering when I don't expect it to?

The following code is a simplification of my current situation. I have a JSON log source which I continuously fetch and write to stdout with puts.
#!/usr/bin/env ruby
require "json"
loop do
puts({ value: "foobar" }.to_json)
sleep 1
end
I want to be able to pipe the output of this script into jq for further processing, but in a 'stream'-friendly way, using unix pipes. Running the above code like so:
./my_script | jq
Results in an empty output. However, if I place an exit statement after the sleep call, the output is sent through the pipe to jq as expected. I was able to solve this problem by calling $stdout.flush following the puts call. While it's working now, I'm not sure why. $stdout.sync is set to true by default (see IO#sync). It seems to me that if sync was enabled, then Ruby should be doing no output buffering, and calling $stdout.flush should not be required - yet it is.
My follow-up question is about using tail instead of jq. It seems to me that I should be able to pipe a text stream into tail the same way I pipe it into jq, but neither method (with the $stdout.flush call or without it) works - the output is just empty.
As #Ry points out in the comments, $stdout.sync is true by default in IRB, but this is not necessarily the same for scripts.
So you should set $stdout.sync = true to be sure to prevent buffering.

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.

return value of bash shell

I am trying to learn linux bash scripting. I have a script and I want to get the return value of this script and store it in a variable.
Any help is welcome.
Thank you in advance.
#!/bin/bash
HOST_NAME=$1
{ echo "105" ; sleep 5; } | telnet $HOST_NAME 9761;
To avoid confusion don't think/talk of it as a return value, think of it as what it is - an exit status.
In most programming languages you can capture the return value of a function by capturing whatever that function returns in a variable, e.g. with a C-like language :
int foo() {
printf("35\n");
return 7;
}
void main() {
int var;
var=foo();
}
the variable var in main() after calling foo() will hold the value 7 and 35 will have been printed to stdout. In shell however with similar-looking code:
foo() {
printf "35\n"
return 7
}
main() {
local var
var=$(foo)
}
var will have the value 35 and the unmentioned builtin variable $? which always holds the exit status of the last command run will have the value 7. If you wanted to duplicate the C behavior where 35 goes to stdout and var contains 7 then that'd be:
foo() {
printf "35\n"
return 7
}
main() {
local var
foo
var=$?
}
The fact that shell functions use the keyword return to report their exit status is confusing at first if you're used to other Algol-based languages like C but if they used exit then it'd terminate the whole process so they had to use something and it quickly becomes obvious what it really means.
So when taking about shell scripts and functions use the words "output" and "exit status", not "return" which some people in some contexts will assume means either of those 2 things, and that'll avoid all confusion.
Btw to avoid making things even more complicated I said above that $? is a variable but it's really the value of the "special parameter" ?. If you really want to understand the difference right now then see https://www.gnu.org/software/bash/manual/bash.html#Shell-Parameters for a discussion of shell parameters which includes "special parameters" like ? and #, "positional parameters" like 1 and 2, and "variables" like HOME and var as used in my script above.
The $? shell variable stores the return value, however with the Linux telnet client this may not be as useful as you think. The client will return 1 if the remote host closes the connection (or any remote or network error occurs) and 0 if the local client side closes the connection successfully. The problem being that many services are written so that they send data and then close the TCP connection themselves without waiting for the client:
$ telnet time-b.timefreq.bldrdoc.gov 13
Trying 132.163.96.2...
Connected to time-b-b.nist.gov.
Escape character is '^]'.
58600 19-04-27 13:56:16 50 0 0 736.0 UTC(NIST) *
Connection closed by foreign host.
$ echo $?
1
Even if the client sends a command to the server to quit over the TCP stream, this still results in the remote side closing the connection, with the same result:
$ telnet mail.tardis.ed.ac.uk 25
Trying 193.62.81.50...
Connected to isolus.tardis.ed.ac.uk.
Escape character is '^]'.
220 isolus.tardis.ed.ac.uk ESMTP Postfix (Debian/GNU)
QUIT
221 2.0.0 Bye
Connection closed by foreign host.
$ echo $?
1
So, you're going to get a 1 no matter what really. If you want the return value of a remote script, this is easier with ssh like this:
$ ssh ssh.tardis.ed.ac.uk "exit 5"
THE TARDIS PROJECT | pubpubpubpubpubpubpubpubpub | Authorised access only
$ echo $?
5
As far as I know the only time telnet would return zero (i.e. success) is if you escape and quite the client, like this:
$ telnet www.google.com 80
Trying 216.58.210.36...
Connected to www.google.com.
Escape character is '^]'.
^]
telnet> quit
Connection closed.
$ echo $?
0
Hope this helps.
It depends on what you mean by return value.
Processes (on UNIX-like systems) can return to a shell a single unsigned byte as an exit status, so that gives a value in the range 0-255. By convention zero means success and any other value indicates a failure.
(In lower-level languages, like C, you can get more than just this exit status, but that's not visible in bash).
The exit status of the last command run is stored in variable ?, so you can get its value from $?, however since many programs only return either 0 (it worked) or 1 (it didn't work), that's not much use.
Bash conditionals, like if and while test for success (exit code of 0) or failure (exit code of non-zero):
if some-command
then
echo "It worked"
else
echo "It didn't work"
fi
However ....
If you mean you want to get the output from the script, that's a different matter. You can capture it using:
var=$(some-command)
But wait, that only captures normal output, routed to a stream called stdout (file descriptor 1), it does not capture error messages which most programs write to a stream called stderr (file descriptor 2). To capture errors as well you need to redirect file descriptor 2 to file descriptor 1:
var=$(some-command 2>&1)
The output text is now in variable var.
The ? variable always stores the exit code of the previous command.
You can retrieve the value with $?.
Some context: http://tldp.org/LDP/abs/html/exit-status.html

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

How should I check for EOF in Ruby

I have a Ruby script (1.8.7) that sets up its own interactive shell for running specific commands.
I want to be able to exit when a user presses CTRL+D (mac/linux). The script just sits in a loop and uses Readline to read user input. I understand that CTRL+D sends the EOF control character but how do I test for this in Ruby? It doesn't seem to be included in the lists of standard unix signals and since technically it isn't a character, I'm guessing normal string comparison on the line won't work either.
Any ideas / pointers / suggestions would be much appreciated
Cheers
From the documentation:
readline(prompt = "", add_hist = false)
(…) Returns nil when the inputted line is empty and user inputs EOF (Presses ^D on UNIX).
Example:
require "readline"
while buf = Readline.readline("> ", true)
p buf
end
puts "EOF received, exiting"

Resources