8

如果我正在编写一个 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"

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

4

6 回答 6

8

鉴于以下红宝石

# 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"

享受!

于 2013-11-07T01:30:28.833 回答
5

这对你不起作用的原因是 b/c rubysystem​​ 在单独的 shell 中运行它的命令。因此,当一个系统命令完成时,为您提供文件的 shell 将关闭,并且该 shell 中设置的任何环境变量都将被遗忘。

如果您直到运行时才知道源文件的名称,那么Roboprog 的答案是一个好方法。但是,如果您提前知道源文件的名称,则可以使用 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​​ 脚本中使用设置的环境变量,而不是在您的 shell 中设置它们(因为一旦 ruby​​ 进程完成它们就会被遗忘)。为此,你被source命令困住了。

于 2009-07-29T14:45:25.553 回答
3

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

#!/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' 是一个非常强大的方法。它很容易跳过这个过程。

于 2014-10-15T11:25:52.470 回答
3

对@takeccho 的回答进行了一些改进......检查和一些口哨声。首先,源环境通过 清理env -i,这是一种安全措施,但在某些情况下可能不需要。其次,通过set -a,文件中设置的所有变量都从 shell 中“导出”,从而导入到 ruby​​ 中。这对于模拟/覆盖在与 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​​ 使用 输出 ENV 对象p时,它以 ruby​​ 可以将其作为对象读回的方式这样做。因此,我们使用 shell 来获取目标文件,并使用 ruby​​(在子 shell 中)以可序列化的形式输出环境。eval然后我们在我们的 ruby​​ 进程中捕获 ruby​​ 的输出和它。显然,这并非没有风险,因此为了降低风险,我们 (1) 验证传入的文件名,以及 (2) 使用正则表达式验证我们从 ruby​​-subshel​​l 得到的东西实际上是,可序列化的哈希字符串。一旦我们确定这一点,我们eval就会创建一个新的哈希。然后我们“手动”将哈希与 合并ENV,这是一个Object而不是常规的Hash。如果它是一个哈希,我们可以使用该#merge!方法。

编辑:sh -a导出的东西,如PATH. 我们还必须在哈希合并之前删除SHLVL和。PWD

于 2015-08-21T21:45:28.283 回答
1

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

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

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

/^(\w+)=(.*)$/

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

(是的,我在对冲我比 Ruby 更了解 Perl 的事实,但方法是一样的)

于 2009-07-29T00:49:27.713 回答
-4
system 'source /file/I/want/to/source.sh'

不确定这是否会满足您的要求。它将在子shell 中执行源命令。尝试一下,看看它是否符合您的要求。

于 2009-07-29T00:22:37.550 回答