`exec` kills script - ruby

Running sdiff through exec causes my script to exit without errors. Even the ensure block does not get run:
begin
puts "I occur"
exec("sdiff onefile.csv anotherfile.csv > filediffs.txt")
rescue Exception => e
puts "I do not get printed"
puts e
ensure
puts "I do not get printed"
end
puts "I used to get printed, repeatedly, now not, repeatedly"
It was working as expected for a while, then it started mysteriously exiting and the conditions are the same. No terminal output after "I occur".

It is the expected behaviour of the exec method, the documentation about it says:
Replaces the current process by running the given external command
You probably want to use system instead of exec.

exec will replace the current process by the command passed as argument to it. After exec() has been executed the calling process won't exist anymore.
Check this for reference and alternatives.

Related

How to check for command return codes inside a ruby shell script?

I have a simple shell script written in ruby that runs some predefined commands and saves the output strings.
The script works well, but I need a way branch conditionally if the command fails. I've tried using the $? object but the script exits before it gets there.
#!/usr/bin/ruby
def run_command(cmd)
`#{cmd}`
if $?.success?
# continue as normal
else
# ignore this command and move on
end
end
run_command('ls')
run_command('not_a_command')
# Output:
# No such file or directory - not_a_command (Errno::ENOENT)...
I've tried $?.exitstatus or even just puts $? but it always exits before it gets there because the script is obviously running the command before hitting that line.
Is there a way to check if the command will run before actually running it in the script?
Hope that's clear, thanks for your time!
Use system (which returns true or false depending on exit code) instead of backticks (which return output string):
if system(cmd)
...
else
...
end
If you want it to run quietly without polluting your logs / output:
system(cmd, out: File::NULL, err: File::NULL)

How to call an external script with arguments?

I have a Ruby script that should call various scripts when the execution throws an error. How can I call a script with two arguments?
For the moment I am using the code below but I need to also send two arguments to script.sh:
my_script = "script.sh"
%x( #{my_script})
You almost figured it out, just add the params:
my_script = "script.sh"
%x( #{my_script} #{arg1} #{arg2})
You may also want to check the return code:
if $? == 0
puts "script executed successfully"
else
puts "script returned an error, exit code: #{$?.to_s.split('exit ')[-1]}"
end
dimid's way would work in the simplest cases, but it will not work in edge cases.
You need to shell-escape the arguments.
require "shellwords"
"command %s %s" % args.map{|arg| Shellwords.escape(arg)}

How is the exit command properly executed in Ruby?

I have been trying to create a Ruby gem that simply exits my terminal whenever "x" is entered. Here is my main project file:
module SwissKnife
VERSION = '0.0.1'
class ConsoleUtility
def exit
`exit`
end
end
end
and my executable:
#!/usr/bin/env ruby
require 'swissknife'
util = SwissKnife::ConsoleUtility.new
util.exit
For some reason whenever I run this nothing appears to happen. I debugged it by adding in a simple puts 'Hello World!' in there, and it would print "Hello World!" but not exit. What am I doing wrong? Any help is greatly appreciated!
Backticks execute code in a new shell
exit isn't an executable that your shell runs, it's a special command understood by your shell - telling it to exit
So when you do
`exit`
it starts a shell which immediately exits. Not very useful. To exit the shell, you can instead kill Ruby's parent process.
Process.kill 'HUP', Process.ppid

Ruby - Open3 not finishing subprocess

I'm using:
- Ruby 1.9.3-p448
- Windows Server 2008
I have a file that contains commands that is used by a program, I'm using it in this way
C:\> PATH_TO_FOLDER/program.exe file.txt
File.txt have some commands so program.exe will do the following:
- Execute commands
- Reads from a DB using an ODBC method used by program
- Outputs result in a txt file
Using powershell this command works fine and as expected.
Now I have this in a file (app.rb):
require 'sinatra'
require 'open3'
get '/process' do
program_path = "path to program.exe"
file_name = "file.txt"
Open3.popen3(program_path, file_name) do |i, o, e, w|
# I have some commands here to execute but just as an example I'm using o.read
puts o.read
end
end
Now when using this by accessing http://localhost/process, Open3 works by doing this (I'm not 100% sure but after trying several times I think is the only option)
Reads commands and executes them (this is ok)
Tries to read from DB by using ODBC method (Here is my problem. I
need to receive some output from Open3 so I can show it in a browser, but I guess when it tries to read it starts another process that Open3 is not aware of, so Open3 goes on and finish without waiting for it)
Exits
Exits
I've found about following:
Use Thread.join (in this case, w.join) in order to wait for process to finish, but it doesn't work
Open4 seems to handle child process but doesn't work on Windows
Process.wait(pid), in this case pid = w.pid, but also doesn't work
Timeout.timeout(n), the problem here is that I'm not sure how long
will it take.
Is there any way of handling this? (waiting for Open3 subprocess so I get proper output).
We had a similar problem getting the exit status and this is what we did
Open3.popen3(*cmd) do |stdin, stdout, stderr, wait_thr|
# print stdout and stderr as it comes in
threads = [stdout, stderr].collect do |output|
Thread.new do
while ((line = output.gets rescue '') != nil) do
unless line.blank?
puts line
end
end
end
end
# get exit code as a Process::Status object
process_status = wait_thr.value #.exitstatus
# wait for logging threads to finish before continuing
# so we don't lose any logging output
threads.each(&:join)
# wait up to 5 minutes to make sure the process has really exited
Timeout::timeout(300) do
while !process_status.exited?
sleep(1)
end
end rescue nil
process_status.exitstatus.to_i
end
Using Open3.popen3 is easy only for trivial cases. I do not know the real code for handling the input, output and error channels of your subprocess. Neither do I know the exact behaviour of your subprocesses: Does it write on stdout? Does it write on stderr? Does it try to read from stdin?
This is why I assume that there are problems in the code that you replaced by puts o.read.
A good summary about the problems you can run into is on http://coldattic.info/shvedsky/pro/blogs/a-foo-walks-into-a-bar/posts/63.
Though I disagree with the author of the article, Pavel Shved, when it comes to finding a solution. He recommends his own solution. I just use one of the wrapper functions for popen3 in my projects: Open3.capture*. They do all the difficult things like waiting for stdout and stderr at the same time.

How to wait for system command to end

I'm converting an XLS 2 CSV file with a system command in Ruby.
After the conversion I'm processing the CSV files, but the conversion is still running when the program wants to process the files, so at that time they are non-existent.
Can someone tell me if it's possible to let Ruby wait the right amount of time for the system command to finish?
Right now I'm using:
sleep 20
but if it will take longer once, it isn't right of course.
What I do specifically is this:
#Call on the program to convert xls
command = "C:/Development/Tools/xls2csv/xls2csv.exe C:/TDLINK/file1.xls"
system(command)
do_stuff
def do_stuff
#This is where i use file1.csv, however, it isn't here yet
end
Ruby's system("...") method is synchronous; i.e. it waits for the command it calls to return an exit code and system returns true if the command exited with a 0 status and false if it exited with a non-0 status. Ruby's backticks return the output of the commmand:
a = `ls`
will set a to a string with a listing of the current working directory.
So it appears that xls2csv.exe is returning an exit code before it finishes what it's supposed to do. Maybe this is a Windows issue. So it looks like you're going to have to loop until the file exists:
until File.exist?("file1.csv")
sleep 1
end
Try to use threads:
command = Thread.new do
system('ruby programm.rb') # long-long programm
end
command.join # main programm waiting for thread
puts "command complete"

Resources