如果我正在编写一个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"
可悲的是,到目前为止还没有快乐。
答案 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-subshell返回的东西实际上是可序列化的哈希字符串。一旦我们确定了这一点,我们就会创建eval
来创建新哈希。然后我们手动"将哈希值与ENV
合并,这是Object
而不是常规Hash
。如果它是Hash,我们可以使用#merge!
方法。
编辑:sh -a
导出了PATH
之类的内容。我们还必须在哈希合并之前删除SHLVL
和PWD
。
答案 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命令。尝试一下,看看它能完成您所追求的目标。