我需要从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吗?
答案 0 :(得分:1)
您需要尝试在要提供的文件前面添加./
,如果子shell为bash(检查$SHELL
),则应该有效。
irb(main):003:0> system("source ./test.sh && echo $TEST && cat test.sh")
test
export TEST=test
=> true
如果$SHELL
为sh
,则您需要执行. ./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
这看起来有点矫枉过正,但它对子进程的控制实际上非常好。
感谢大家的建议。