源shell脚本进入ruby脚本中的环境

时间:2009-07-28 23:09:05

标签: ruby shell scripting environment

如果我正在编写一个shell脚本并且我想“获取”一些外部(c-)shell脚本来设置我的环境,我可以像这样调用:

source /file/I/want/to/source.csh

我想用ruby脚本替换执行此操作的shell脚本。我可以在ruby脚本中做类似的事情吗?

更新

刚试用test_script.csh:

#!/bin/csh

setenv HAPPYTIMES True

...和test_script.rb:

#!/usr/bin/env ruby
system "~/test_script.csh"
system "echo $HAPPYTIMES"

可悲的是,到目前为止还没有快乐。

6 个答案:

答案 0 :(得分:6)

鉴于以下Ruby

# Read in the bash environment, after an optional command.
#   Returns Array of key/value pairs.
def bash_env(cmd=nil)
  env = `#{cmd + ';' if cmd} printenv`
  env.split(/\n/).map {|l| l.split(/=/)}
end

# Source a given file, and compare environment before and after.
#   Returns Hash of any keys that have changed.
def bash_source(file)
  Hash[ bash_env(". #{File.realpath file}") - bash_env() ]
end

# Find variables changed as a result of sourcing the given file, 
#   and update in ENV.
def source_env_from(file)
  bash_source(file).each {|k,v| ENV[k] = v }
end

以及以下test.sh:

#!/usr/bin/env bash
export FOO='bar'

你应该得到:

irb(main):019:0> source_env_from('test.sh')
=> {"FOO"=>"bar"}
irb(main):020:0> ENV['FOO']
=> "bar"

享受!

答案 1 :(得分:4)

这不适合你的原因是b / c ruby​​在单独的shell中运行其system命令。因此,当一个系统命令完成时,源文件的shell将关闭,并且忘记在该shell中设置的任何环境变量。

如果在运行时之前不知道源文件的名称,那么Roboprog's answer是一种很好的方法。但是,如果您提前知道源文件的名称,则可以使用hashbang行快速破解。

% echo sourcer.rb
#!/usr/bin/env ruby
exec "csh -c 'source #{ARGV[0]} && /usr/bin/env ruby #{ARGV[1]}'"
% echo my-script.rb
#!/usr/bin/env ruby sourcer.rb /path/to/file/I/want/to/source.csh
puts "HAPPYTIMES = #{ENV['HAPPYTIMES']}"
% ./my-script.rb
HAPPYTIMES = True

所有这些只会帮助你在ruby脚本中使用set enviroment变量,而不是在shell中设置它们(因为一旦ruby过程完成就会忘记它们)。为此,你坚持使用source命令。

答案 2 :(得分:2)

我有同样的问题。我解决如下。

#!/usr/local/bin/ruby

def source(filename)
  ENV.replace(eval(`tcsh -c 'source #{filename} && ruby -e "p ENV"'`))
end

p "***old env*****************************"
p ENV
source "/file/I/want/to/source.csh"
p "+++new env+++++++++++++++++++++++++++++"
p ENV

' EVAL'是一种非常强大的方法。 它很容易跳过这个过程。

答案 3 :(得分:2)

改善@ takeccho的答案......检查,和几个口哨。首先,通过env -i清理源环境,这是一种安全措施,但在某些情况下可能不需要。其次,通过set -a,文件中设置的所有变量都是"导出"从壳中进而导入红宝石。这对于在init脚本和systemd env文件中使用的环境文件中的模拟/覆盖行为非常有用。

def ShSource(filename)
  # Inspired by user takeccho at http://stackoverflow.com/a/26381374/3849157
  # Sources sh-script or env file and imports resulting environment
  fail(ArgumentError,"File #{filename} invalid or doesn't exist.") \
     unless File.exist?(filename)

  _newhashstr=`env -i sh -c 'set -a;source #{filename} && ruby -e "p ENV"'`
  fail(ArgumentError,"Failure to parse or process #{filename} environment")\
     unless _newhashstr.match(/^\{("[^"]+"=>".*?",\s*)*("[^"]+"=>".*?")\}$/)

  _newhash=eval(_newhashstr)
   %w[ SHLVL PWD _ ].each{|k|_newhash.delete(k) }
  _newhash.each{|k,v| ENV[k]=v } # ENV does not have #merge!
end

操作原理:当ruby使用p输出ENV对象时,它会以ruby可以作为对象读回来的方式这样做。因此,我们使用shell来获取目标文件,并使用ruby(在子shell中)以可序列化的形式输出环境。然后我们在ruby过程中捕获ruby的输出和eval。显然,这并非没有风险,所以为了降低风险,我们(1)验证传入的文件名,以及(2)使用正则表达式验证我们从ruby-subshel​​l返回的东西实际上是可序列化的哈希字符串。一旦我们确定了这一点,我们就会创建eval来创建新哈希。然后我们手动"将哈希值与ENV合并,这是Object而不是常规Hash。如果它是Hash,我们可以使用#merge!方法。

编辑:sh -a导出了PATH之类的内容。我们还必须在哈希合并之前删除SHLVLPWD

答案 4 :(得分:1)

您将不得不编写一个函数来运行类似下面的内容,并捕获输出(“反引号”操作):

/bin/csh -e '. my_script ; env'

在每一行上循环,匹配类似

的内容
/^(\w+)=(.*)$/

然后使用第一个匹配捕获作为var名称,第二个捕获作为var值。

(是的,我正在考虑这样一个事实,即我知道Perl比Ruby更好,但方法也是一样的)

答案 5 :(得分:-4)

system 'source /file/I/want/to/source.sh'

不确定这会做你想做的事。它将在子shell中执行source命令。尝试一下,看看它能完成您所追求的目标。