对@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-subshell 得到的东西实际上是,可序列化的哈希字符串。一旦我们确定这一点,我们eval
就会创建一个新的哈希。然后我们“手动”将哈希与 合并ENV
,这是一个Object
而不是常规的Hash
。如果它是一个哈希,我们可以使用该#merge!
方法。
编辑:sh -a
导出的东西,如PATH
. 我们还必须在哈希合并之前删除SHLVL
和。PWD