Reputation: 1148
What I need is:
Execute something before calling a system command.
Execute my system command
that involve prompting and getting answers from the user
keeping the effects of ctrl-c on the called command intact
Get the result of my system command and carry on with more ruby code execution
So far I tried something that looks like:
#!/usr/bin/env ruby
p "Foo"
exit_value = exec 'heroku run console'
p "Bar"
exit exit_value
This one fails because exec replaces and terminate current process, so no more ruby code is executed after exec
I've already read this post: How to run code after ruby Kernel.exec
And I tried to make do with a Kernel#system call:
#!/usr/bin/env ruby
p "Foo"
system 'heroku run console'
p "Bar"
exit $?
This one also fails, because ctrl-c is apparently caught by my ruby process and kills it instead of reaching its intended target.
So, is there a way to deal with these peculiar requirements?
Upvotes: 2
Views: 1954
Reputation: 1148
Thanks a lot to hek2mgl for pointing in the right direction:
include Signal
include Process
# Handling SIGINT by doing nothing prevents default behaviour
# (killing both processes)
Signal.trap("INT") {}
# Fork off subprocess (so exec won't exit from your main process)
pid = fork
if pid == nil then
# Child code. Use exec(!) to avoid the signal handler
# getting called in the child.
exec 'heroku run console'
else
# Wait for subprocess to exit
wait pid
# "wait" sets the $? according to the subprocess exit status
exit_status = $?.exitstatus
p "Execute more ruby code"
exit exit_status
end
Upvotes: 1
Reputation: 158020
I would install a signal trap for SIGINT
, fork off the sub process, exec the command (to prevent the signal handler from running in parent and child) and kill the subprocess if SIGINT
occurs:
include Signal
include Process
# Define a signal handler for SIGINT
Signal.trap("INT") do
if $pid != nil then
# Kill the subprocess
Process.kill("INT", $pid)
else
# Terminate ourself
exit 1
end
end
# Fork off subprocess
# The $ marks the variable as global
$pid = fork
if $pid == nil then
# Child code. Use exec(!) to avoid the signal handler
# getting called in the child.
exec 'bash -c "echo test; sleep 3; echo test"'
end
# Wait for subprocess to exit
wait $pid
# Reset $pid
$pid = nil
exit_status = $?.exitstatus
# Detect whether the child process has been killed
if exit_status == nil then
p "Child process has been killed."
end
p "Execute more ruby code"
...
Upvotes: 0