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



我有一系列bash文件,我想将它们源到当前的Ruby环境中。这里有一个例子:

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

有没有一种方法可以将foo.sh源到父环境中,而不必手动解析它?

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

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

#!/bin/sh
source foo.sh
ruby some_ruby_file.rb

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

使用如上所述的包装器,或者想出一种不同的方法来保存环境配置,例如YAML文件或其他一些传统的配置解决方案。

TL;DR

您不能从子进程或子shell更改父进程的环境,但您可以将文件源代码或解析到当前进程中。

"源"Ruby脚本

您可以使用Kernel#load或Kernel#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 /exports+(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#扫描方法找到的每个变量添加到ENV中,然后可以通过其键访问它。

解析注意事项

  • 这在随意测试中很好,但如果您没有在定义变量的同一行导出变量,则可能需要修改正则表达式
  • 此外,由对输入进行消毒或验证
  • 我强烈建议只设置您期望看到的环境变量(例如,使用Array#select将可接受的ENV密钥列入白名单),并确保您设置的每个变量的值对于您的特定用例是合理和安全的

一个更好的选项

通常,如果您试图在脚本中设置配置选项,最好加载一个YAML文件或使用OptionParser。尤其是YAML文件易于阅读、易于解析、可人工编辑,并且(相对)易于清理。您的里程数可能有所不同。

得到了一些几乎正确的答案,没有一个完全适用于我的情况——这是我最终要做的:

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)。我试着使用这个技巧。如何在Ruby脚本中为命令shell获取环境变量?但是set为您提供了所有环境变量,我只想要该文件中的那些。

吉姆的主意很好,但由于我的限制而无法使用。CodeGnome走在正确的道路上,但我们不能在不评估的情况下大规模读取文件,否则我们会错过file = Pathname.new "tmp-variables.txt"之类的东西。谢谢大家,这是一个相当团队的努力。我已经给了你们所有的选票。

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

https://github.com/bkeepers/dotenv

相关内容

  • 没有找到相关文章

最新更新