2

I need to run a shell command from a ruby application. I'm using system() but this also applies to backticks.

When running my command, I need to load a shell script first that sets up some things so I try something like this:

system("source my_script.sh && my_command")

On my Mac laptop this works as intended but on my ubuntu server I get:

sh: 1: source: not found

I was wondering about the "sh" in there since my shell should be a bash, so tried this:

system("echo $SHELL && source my_script.sh && my_command")

Which gives me:

/bin/bash
sh: 1: source: not found

So, it is using the right shell but for some reason, source does not work.

Why? And what can I do about it?

Update As Sergio Tulentsev pointed out, Ruby does not necessarily use the shell that is set in $SHELL.

This gave me the actual shell that ruby was using:

system("ps -p $$ | tail -1 | awk '{print $NF}'")
sh
 => true

So, it's using sh. Can I somehow force it to use bash?

3
  • 2
    If $SHELL is set to bash, it doesn't necessarily mean that ruby will use it, I think. Try this tip to determine actual shell: liquidat.wordpress.com/2007/07/28/… Commented Mar 25, 2014 at 9:41
  • Oh, I didn't know that. But yes, it's actually "sh". Thanks. Commented Mar 25, 2014 at 9:44
  • 1
    If $SHELL is sh, then source wont work, you'll need to de . file.sh to source it Commented Mar 25, 2014 at 9:49

2 Answers 2

2

You need to try adding ./ in front of the file you want to source, that should work if the subshell is bash (check $SHELL).

irb(main):003:0> system("source ./test.sh && echo $TEST && cat test.sh")
test
export TEST=test
=> true

If $SHELL is sh, then you need to do . ./test.sh instead of source ./test.sh, as the source keyword is bash only.

Or you can make sure that you are using bash, by doing:

irb(main):007:0> system("/bin/bash -c 'source ./test.sh && echo $TEST && cat test.sh'")
test
export TEST=test
=> true
Sign up to request clarification or add additional context in comments.

3 Comments

Nope, that's not it. Even with absolute paths it doesn't work.
Thanks, that kind of works but brings me to escaping problems with my command string. I think I'll spawn /usr/bin/env bash with Open3 to have finer control over it.
That sounds like a good idea. As for the escaping, you should probably wrap you Open3 call, so it takes care of that.
1

As others have pointed out, Ruby uses sh for its subshells. One way to make it use bash would be something like system("/bin/bash -c '...'") which leads to all kinds of escaping problems. Instead I decided to use Open3 to spawn a "real" process, run bash in it and pipe my commands into it. Works like a charm:

require "open3"

# using bash --login to ensure the same env as usual
Open3.popen3('/usr/bin/env bash --login') do |stdin, stdout, stderr, wait_thr|
  pid = wait_thr[:pid]

  stdin.puts("cd some_directory")
  stdin.puts("source some_script")
  stdin.puts("some_command")

  # don't forget to close it again
  stdin.puts("exit")

  # for debug purposes
  stdout.each_line do |line|
    puts "STDOUT: " + line
  end

  stdin.close
  stdout.close
  stderr.close
end

This may seem like a little overkill but the control it allows over the child process is actually pretty nice.

Thanks everybody for your suggestions.

1 Comment

You make a good point about the escaping problems. But the code you show also exhibits those escaping problems, doesn't it? I don't see how I can have the safe escaped command syntax via arrays when all I'm doing is putting commands on stdin. Maybe I'm missing something here.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.