How is the exit command properly executed in Ruby? - 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

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)

Exit program and open file

Hello I am stuck in a situation where I need to run a command after my program exits.
Reason :
MyApp1.exe needs to be updated to a new version MyApp2.exe. So, MyApp1.exe should be terminated before I start MyApp2.exe, else inno setup will not let me install the new version as the old version is still running.
File.open("MyApp2.exe", "wb") do |saved_file|
open("http://example.com/MyApp2.exe", "rb") do |read_file|
saved_file.write(read_file.read)
end
end
`start "" "MyApp2.exe"`
exit
Now the start command is before exit, as I want to start the new downloaded MyApp2.exe, but I would like to exit first and then start the installer.
Please help
I think this can be achieved using Kernel::exec instead of backticks or the system method. Unlike other shell-invoking commands, exec replaces the currently running process (i.e. the Ruby interpreter) with the new process.
Your whole script would then be
File.open("MyApp2.exe", "wb") do |saved_file|
open("http://example.com/MyApp2.exe", "rb") do |read_file|
saved_file.write(read_file.read)
end
end
exec 'start "" "MyApp2.exe"'
You cannot continue with any statements following exec, the program will exit at that point.

`exec` kills script

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.

Ruby file output from fork

I have one simple script:
fork do
STDOUT.reopen(File.open('/tmp/log', 'w+'))
STDOUT.sync = true
exec 'bundle exec ruby script.rb'
end
script.rb:
loop do
sleep 1
puts "MESSAGE"
end
When it work, all outputs is buffering(?) and writes to /tmp/log by big pices.
It works only if I modify script:
$stdout.puts "MESSAGE"
$stdout.flush
How can I do the same without modifying script.rb ?
Thanks.
When you call exec, you create a new process, and although this process inherits the file you set as standard out, it doesn't inherit the other settings, in particular the sync setting.
In order to get unbuffered output in the new process, you need to set it in that process. If you don't want to modify script.rb one workaround could be to create another file, named somethig like sync.rb containing just:
STDOUT.sync = true
which you can then require when running your command:
exec 'bundle exec ruby -r./sync script.rb'
The new Ruby process will now require sync.rb, which simply sets sync mode on STDOUT to true before executing your script.

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