如何将环境变量从shell脚本导入当前的Ruby环境?

时间:2014-02-26 16:36:19

标签: ruby bash environment-variables

我有一系列bash文件,我想在当前的Ruby环境中获取。这是一个例子:

$ echo "export FOO=bar" > foo.sh
$ irb
> `source $(pwd)/foo.sh`
> puts ENV['FOO']
=> nil

有没有办法将foo.sh导入父环境而无需手动解析它?

4 个答案:

答案 0 :(得分:2)

Ruby中的所有shell调用都在子进程中运行,无论是使用system()还是使用反引号或Process或任何其他机制来执行它们。像这样的子进程不可能影响当前的Ruby进程。

如果要在执行某些Ruby代码之前获取shell脚本,可以创建一个包装器:

#!/bin/sh

source foo.sh
ruby some_ruby_file.rb

如果你真的想,你可以尝试解析shell脚本中的变量导出,然后直接设置Ruby的ENV哈希,但这几乎肯定是个坏主意。它很难写,容易出错,而且不可维护。

使用上面的包装器,或者以不同的方式保存环境配置,例如YAML文件或其他传统配置解决方案。

答案 1 :(得分:2)

TL; DR

您无法从子进程或子shell更改父进程的环境,但您当然可以将文件源或解析为当前进程。

“源”是一个Ruby脚本

您可以使用Kernel#loadKernel#require方法获取Ruby脚本。这会将文件的内容导入当前进程。

将Shell脚本解析为Ruby

如果源文件是shell脚本,则不能简单地将其加载为Ruby;你需要对文件进行某种解析。这可能存在安全风险,除非您信任您正在阅读的文件的内容,格式和来源。

假设您信任您的输入源,并提供如下示例文件:

#!/usr/bin/bash

export FOO='bar'
echo FOO
echo bar
echo "FOO=$FOO"
你可以这样做:

# Find variables in the general form of "export x=y" 
env_vars = File.read('/tmp/file.txt').scan /export\s+(\S+)=(\S+)/
#=> [["FOO", "'bar'"]]

# Parse each variable into the Ruby ENV key/value pair, removing
# outer quotes on the value if present.
env_vars.each { |v| ENV[v.first] = v.last.gsub /\A['"]|['"]\Z/, '' }

# Verify you have the value you expect.
ENV['FOO']
#=> "bar"

这会将通过String#scan方法找到的每个变量添加到ENV,然后可以通过其密钥访问它。

解析注意事项

  • 这在临时测试中可以正常工作,但如果您没有在定义它的同一行上导出变量,则可能需要修改正则表达式。
  • 此外,您可以 来清理或验证输入。
  • 我强烈建议您只设置您期望看到的环境变量(例如,使用Array#select将可接受的ENV键列入白名单),并确保您设置的每个变量的值对于您的特定用例是理智且安全的。

更好的期权选择

通常,如果您尝试从脚本内部设置配置选项,最好加载YAML file或使用OptionParser。特别是YAML文件易于阅读,易于解析,人类可编辑,并且(相对)易于消毒。您的里程可能会有所不同。

答案 2 :(得分:0)

得到几个几乎答案,没有一个完全适用于我的案例,这就是我最终做的事情:

file = Pathname.new "tmp-variables.txt"
`env -i /bin/sh -c 'source <myfile-here.sh> && env > #{file}'`
file.each_line do |line|
  line.match(/(?<key>[^=]+)=(?<value>.+)/) {|match| ENV[match[:key]] = match[:value] }
end

以下是一个示例脚本,显示了它的实际效果:

require 'pathname'

file = Pathname.new "tmp-variables.txt"
`mkdir profile.d`
`echo "export FOO=${FOO:-'bar'}" > profile.d/node`
`touch #{file}`
`env -i /bin/sh -c 'source profile.d/node && env > #{file}'`
file.each_line do |line|
  line.match(/(?<key>[^=]+)=(?<value>.+)/) {|match| ENV[match[:key]] = match[:value] }
end
puts ENV['FOO']

这里的一些技巧env -i执行命令(-c)而没有任何环境变量。我尝试使用此技巧How do I source environment variables for a command shell in a Ruby script?,但set为您提供所有环境变量,我只想要该文件中的那些。

Jim的想法很好,但由于我的限制而无法使用。 CodeGnome是在正确的道路上,但我们不能在没有评估的情况下阅读文件,否则我们会错误地使用file = Pathname.new "tmp-variables.txt"之类的东西。谢谢大家,这是团队的努力。我已经把你所有的选票都给了你。

答案 3 :(得分:0)

您可以使用dotenv gem导入.env文件。

https://github.com/bkeepers/dotenv