41

How do I export an environment variable from within a Ruby script to the parent shell? For example, implementing a naïve implementation of the read Bash builtin:

#!/usr/bin/ruby

varname = ARGV[0]
ENV[varname] = STDIN.gets  # but have varname exported to the parent process
wilhelmtell
  • 57,473
  • 20
  • 96
  • 131
  • 2
    I've seen [a similar question](http://stackoverflow.com/questions/190168/persisting-an-environment-variable-through-ruby) but I'm not satisfied with the platform-specific answer. – wilhelmtell Apr 18 '10 at 00:09

4 Answers4

40

You can't export environment variables to the shell the ruby script runs in, but you could write a ruby script that creates a source-able bash file.

For example

% cat set_var.rb
#!/usr/bin/env ruby
varname = ARGV[0]
puts "#{varname}=#{STDIN.gets.chomp}"
% set_var.rb FOO
1
FOO=1
% set_var.rb BAR > temp.sh ; . temp.sh
2
% echo $BAR
2
%

Another alternative is that using ENV[]= does set environment variables for subshells opened from within the ruby process. For example:

outer-bash% cat pass_var.rb
#!/usr/bin/env ruby
varname = ARGV[0]
ENV[varname] = STDIN.gets.chomp
exec '/usr/bin/env bash'
outer-bash% pass_var.rb BAZ
quux
inner-bash% echo $BAZ
quux 

This can be quite potent if you combine it with the shell's exec command, which will replace the outer-shell with the ruby process (so that when you exit the inner shell, the outer shell auto-exits as well, preventing any "I thought I set that variable in this shell" confusion).

# open terminal
% exec pass_var.rb BAZ
3
% echo $BAZ
3
% exit
# terminal closes
Steve Folly
  • 8,327
  • 9
  • 52
  • 63
rampion
  • 87,131
  • 49
  • 199
  • 315
38

Simple answer: You can't.

Longer answer: You can't, unless the operating environment provides hooks to do so. Most do not. The best you can usually do is print out the assignments you want done and have the parent execute them.

Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
10

I just tried this and it looks good.

cmd = "echo \"FOO is \\\"$FOO\\\"\"";                                
system(cmd);

# Run some Ruby code (same program) in the child process
fork do
    puts "In child process. parent pid is #$$"
    ENV['FOO']='foo in sub process';
    system(cmd);
    exit 99
end
child_pid = Process.wait
puts "Child (pid #{child_pid}) terminated with status #{$?.exitstatus}"

system(cmd);

This seems to work well - at least on MacOSX

I get

FOO is ""
In child process. parent pid is 1388
FOO is "foo in sub process"
Child (pid 1388) terminated with status 99
FOO is ""

Seems nice in it restores prior state automatically

Ok - now tried a different one as this doesn't spawn 2 subprocesses

Use Process.spawn(env,command)

pid = Process.spawn({ 'FOO'=>'foo in spawned process'}, cmd );
pid = Process.wait();  

This acts like the C system call and allows you to specify pipes and all that other stuff too.

peterk
  • 5,136
  • 6
  • 33
  • 47
  • To save me the time, does anyone know if this works on Linux? Also, I don’t understand how this works, given how child processes inherit their parents’ env but generally cannot modify it. IIRC. – sming Jan 02 '23 at 17:26
-1

What about in ruby printing out standard export code :

puts "export MYVAR=value"

and then using shell backtick to get it executed at shell comands:

$ `./myscript.rb` 

this will take the output of the script and execute it, works in modern shells like bash and zsh

Seeder
  • 1
  • 1