How to Run Code After Ruby Kernel.Exec

How to run code after ruby Kernel.exec

You cannot.

Per the documentation for Kernel#exec, "[it] replaces the current process by running the given external command". That means that you are no longer running your code but instead the code you specified by the command.

If you want to "wrap" a system call then you should use Kernel#system (or the backtick operator) to execute the command in a subshell.

Run a command in current terminal in ruby then execute code when it exits

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

why ruby code crash/exit after my second exec call?

Kernel#exec replaces the current running process. Once it's executed, the remaining part of the code is not run.

puts "start"
ret1 = exec('pwd') # <---- After this, no more remaining code is executed.
...

If you want to get the output of the command, use Kernel#` instead:

puts "start"
ret1 = `pwd`

ruby backtick, kernel.system, exec , command does not succeed

Turns out the issue was I was not closing the files I was creating.

Apparently that was fine when I was invoking it directly from the shell or directly from .sh, because the ruby script had already exited.

However when I would invoke this from the Ruby script it would not be able to access the file and would create issues.

Bottom line: Close your files else loose half day of work !

Ruby - Using Kernel.exec with custom STDOUT

What gets preserved in the other process is the underlying file descriptor (or rather they are hooked up under the hood), so as I commented I don't think you'll ever get writes to that descriptor to be funnelled through your write method - exec replaces the running process with a new one.

A possible approach is to create a pipe, pass one end to your child process and then read from the other end, inserting prefixes as needed,

For example you might do

IO.pipe do |read_pipe, write_pipe|
fork do
exec("echo hello", out: write_pipe)
end
write_pipe.close
while line = read_pipe.gets
puts "prefix: #{line}"
end
end

You might also be interested in IO.popen which wraps some of this up.

How to call shell commands from Ruby

This explanation is based on a commented Ruby script from a friend of mine. If you want to improve the script, feel free to update it at the link.

First, note that when Ruby calls out to a shell, it typically calls /bin/sh, not Bash. Some Bash syntax is not supported by /bin/sh on all systems.

Here are ways to execute a shell script:

cmd = "echo 'hi'" # Sample string that can be used
  1. Kernel#` , commonly called backticks – `cmd`

    This is like many other languages, including Bash, PHP, and Perl.

    Returns the result (i.e. standard output) of the shell command.

    Docs: http://ruby-doc.org/core/Kernel.html#method-i-60

    value = `echo 'hi'`
    value = `#{cmd}`
  2. Built-in syntax, %x( cmd )

    Following the x character is a delimiter, which can be any character.
    If the delimiter is one of the characters (, [, {, or <,
    the literal consists of the characters up to the matching closing delimiter,
    taking account of nested delimiter pairs. For all other delimiters, the
    literal comprises the characters up to the next occurrence of the
    delimiter character. String interpolation #{ ... } is allowed.

    Returns the result (i.e. standard output) of the shell command, just like the backticks.

    Docs: https://docs.ruby-lang.org/en/master/syntax/literals_rdoc.html#label-Percent+Strings

    value = %x( echo 'hi' )
    value = %x[ #{cmd} ]
  3. Kernel#system

    Executes the given command in a subshell.

    Returns true if the command was found and run successfully, false otherwise.

    Docs: http://ruby-doc.org/core/Kernel.html#method-i-system

    wasGood = system( "echo 'hi'" )
    wasGood = system( cmd )
  4. Kernel#exec

    Replaces the current process by running the given external command.

    Returns none, the current process is replaced and never continues.

    Docs: http://ruby-doc.org/core/Kernel.html#method-i-exec

    exec( "echo 'hi'" )
    exec( cmd ) # Note: this will never be reached because of the line above

Here's some extra advice:
$?, which is the same as $CHILD_STATUS, accesses the status of the last system executed command if you use the backticks, system() or %x{}.
You can then access the exitstatus and pid properties:

$?.exitstatus

For more reading see:

  • http://www.elctech.com/blog/i-m-in-ur-commandline-executin-ma-commands
  • http://blog.jayfields.com/2006/06/ruby-kernel-system-exec-and-x.html
  • http://tech.natemurray.com/2007/03/ruby-shell-commands.html

Getting output of system() calls in Ruby

I'd like to expand & clarify chaos's answer a bit.

If you surround your command with backticks, then you don't need to (explicitly) call system() at all. The backticks execute the command and return the output as a string. You can then assign the value to a variable like so:

output = `ls`
p output

or

printf output # escapes newline chars

Restart ruby program

Just run Kernel.exec function that will replace running process with the new one.

The advantage of exec function is that there's no time span when two different bots are run simultaneously. It was one process and after exec call it's instantly replaced with the new one.

Run a shell script from ruby

You can do this a few different ways

Kernel.system "command"
%x[command]
`command`


Related Topics



Leave a reply



Submit