我有一系列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