"源"在红宝石的子壳中

时间:2014-03-25 09:37:44

标签: ruby shell

我需要从ruby应用程序运行shell命令。我正在使用system(),但这也适用于反叛。

运行我的命令时,我需要首先加载一个shell脚本来设置一些东西,所以我尝试这样的事情:

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

在我的Mac笔记本电脑上按预期工作,但在我的ubuntu服务器上我得到:

sh: 1: source: not found

我想知道那里的“sh”,因为我的shell应该是bash,所以试过这个:

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

这给了我:

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

所以,它使用的是正确的shell,但出于某种原因,source不起作用。

为什么呢?我能做些什么呢?

更新 正如Sergio Tulentsev指出的那样,Ruby不一定使用$ SHELL中设置的shell。

这给了我ruby正在使用的实际shell:

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

所以,它正在使用sh。我可以以某种方式强迫它使用bash吗?

2 个答案:

答案 0 :(得分:1)

您需要尝试在要提供的文件前面添加./,如果子shell为bash(检查$SHELL),则应该有效。

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

如果$SHELLsh,则您需要执行. ./test.sh而不是source ./test.sh,因为源关键字仅为bash。

或者您可以通过以下方式确保使用bash:

irb(main):007:0> system("/bin/bash -c 'source ./test.sh && echo $TEST && cat test.sh'")
test
export TEST=test
=> true

答案 1 :(得分:1)

正如其他人所指出的,Ruby使用sh作为其子shell。使用bash的一种方法就是system("/bin/bash -c '...'"),它会导致各种逃避问题。相反,我决定使用Open3来产生一个真实的"进程,在其中运行bash并将命令传递给它。像魅力一样:

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

这看起来有点矫枉过正,但它对子进程的控制实际上非常好。

感谢大家的建议。