【问题标题】:How can I import environment variables from a shell script into the current Ruby environment?如何将环境变量从 shell 脚本导入到当前的 Ruby 环境中?
【发布时间】:2014-03-29 15:28:58
【问题描述】:

我有一系列 bash 文件,我想将它们导入当前的 Ruby 环境。这是一个例子:

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

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

【问题讨论】:

  • 不,你不能,子进程不能改变其父进程的环境。 source 命令在子进程中执行,因此您不能使用它来更改当前进程的环境。
  • 不,您需要解析foo.sh。请参阅this answer 了解更多操作方法。
  • 虽然@toro2k 是正确的,但如果您获取文件,然后将其他命令链接在一起,例如system "source some_sh_file.sh && my_func_in_some_sh_file #{arg}",您可以访问它。这看起来相当不方便,但您可以包装系统调用并将其放在命令之前的源声明之前。
  • 看来这是个不错的方法:stackoverflow.com/questions/2139080/…发帖前没看到。

标签: ruby bash environment-variables


【解决方案1】:

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

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

#!/bin/sh

source foo.sh
ruby some_ruby_file.rb

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

要么使用上述包装器,要么想出不同的方式来保存您的环境配置,例如 YAML 文件或其他一些常规配置解决方案。

【讨论】:

    【解决方案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 文件易于阅读、易于解析、人工可编辑且(相对)易于清理。您的里程可能会有所不同。

    【讨论】:

      【解决方案3】:

      几乎得到了一些答案,没有一个完全适合我的情况,这就是我最终要做的:

      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" 之类的东西。谢谢大家,这是一个团队的努力。我给了你们所有人的赞成票。

      【讨论】:

        【解决方案4】:

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

        https://github.com/bkeepers/dotenv

        【讨论】:

          猜你喜欢
          • 2014-02-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-09-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多