Using ` and the bash source command

Hi all,

I have this code :
source ~/.bash_profile

As part of a script. But when this line executes I get a sh: command
not found. This looks to me like ruby’s ` is running /bin/sh instead
of /bin/bash

So a bit of searching on google and I discover that it’s possible to
do something like:
%x{/bin/bash -c “source ~/.bash_profile;”}

This doesn’t fail with the command not found, but it doesn’t actually
source the file - so it’s fairly useless.

Does anyone have a workaround for getting ruby to exec the bash source
command correctly? I think I’ve tried every combination in the docs
and nothing seems to work.

Thanks,
Kev

On Jun 9, 2:49 pm, “Kevin J.” [email protected] wrote:

do something like:
Kev
Hi,

I’m not sure what you’re trying to achieve by the above. If you say
the second command (the %x one) doesn’t fail, it probably means that
it is sourcing (i.e. running in the same bash process)
the .bash_profile file. But: when you call an OS-level command (in
this case, bash) like this, what happens is that Ruby runs that
command as a child process. So, it is running bash in a child process,
(which in turn reads and executes the commands in .bash_profile), then
returning to your Ruby script - the net effect being that nothing
seems to happen (because the settings done to environment variables
(in the profile file) in a child process, cannot affect the values of
environment variables in the calling (parent) process - both the child
and the parent have their own independent copies of these variables.
The child initially inherits the values for these variables from the
parent when the child is started, but there is no straightforward way
for any changes made to these variables in the child process, to be
propagated back to the same variables in the parent (though there are
workarounds, e.g. you could write the names and values of the env.
vars. to a file in the child process, and then when the child
terminates, read those values from the file back into the
corresponding env. vars. in the parent.

A more typical use of your command would be to use bash, (possibly
“sourcing” the .bash_profile - about which see below), to run a bash
script which does something you need done (e.g. sorting a file) - then
return to your Ruby script. Such changes would be permanent and
visible to the parent.

Sourcing the .bash_profile should actually happen by default when you
run “bash -c” if I remember right, but could be wrong - there are
multiple bash startup files e.g. (.bash_profile. .bash_rc), and I
don’t remember or am not sure which one(s) get executed when you run
bash non-interactively from another script) - need to read the bash
man page for that.

HTH
Vasudev Ram
Dancing Bison Enterprises

On 6/9/07, Kevin J. [email protected] wrote:

do something like:
%x{/bin/bash -c “source ~/.bash_profile;”}

This doesn’t fail with the command not found, but it doesn’t actually
source the file - so it’s fairly useless.

Does anyone have a workaround for getting ruby to exec the bash source
command correctly? I think I’ve tried every combination in the docs
and nothing seems to work.

Because source isn’t a command, it’s a bash builtin,

rick@bill:~$ man source
No manual entry for source
rick@bill:~$ which source
rick@bill:~$

An excerpt from man bash

SHELL BUILTIN COMMANDS

source filename [arguments]
Read and execute commands from filename in the current
shell
environment and return the exit status of the last
command exee$B!>e(B
cuted from filename.

The source built-in in bash is roughly equivalent to include in C, or
load in Ruby.

Now I think that:
%x{/bin/bash -c “source ~/.bash_profile;”}
%x{/bin/bash -c “~/.bash_profile;”}

do pretty much the same thing, they will execute ~/.bash_profile

The problem is that most bash profiles are used primarily to set
environment variables within a bash process, any such variables would
only be in the environments of the bash process and it’s children, not
the parent process.

What effect were you expecting to see?


Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

On 6/9/07, Kevin J. [email protected] wrote:

So my code essentially builds a menu of all the different jdks
installed and then when the user picks an option (eg java-gcj), the
code writes the correct JAVA_HOME out to .bash_profile ready for use

The final step was to source the .bash_profile so that I can just
choose a jdk and be immediately ready to use it

Attached the code (it’s not OO at all, just a simple script):

I’m afraid that this approach won’t work no matter what language you
try to do it with. It’s inherent with the nature of environment
variables.

When you run your little gui program, it gets a copy of the parent
process’s (i.e the bash shell) environment values. Any changes will
only affect the environment of this child process and child processes
it subsequently starts.

If you were to run your Java program itself from ruby, I would think
that you could set the JAVA_HOME environment variable directly with
ENV[JAVA_HOME] = whatever you want, before executing Java as a
child process of the Ruby program.


Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

Hi,

What effect were you expecting to see?

This may seem fairly trivial (and it is), but I’m writing a jdk/java
switcher app as I spend a lot of time working on open source java and
I need to test on multiple jdks. Now previously I’ve always opened vi
and made the changes manually, but I decided that a tiny gui program
would give me a chance to do a bit of ruby for a change, and I could
get some more experience with Gtk.

So my code essentially builds a menu of all the different jdks
installed and then when the user picks an option (eg java-gcj), the
code writes the correct JAVA_HOME out to .bash_profile ready for use

The final step was to source the .bash_profile so that I can just
choose a jdk and be immediately ready to use it

Attached the code (it’s not OO at all, just a simple script):

#!/usr/bin/ruby

=begin
jdk_switcher.rb - Ruby/Gnome2 JDK switching tool
Copyright © 2007 Kevin J.
This program is licenced under the Apache Software Licence 2.0
=end

require ‘gtk2’

def set_java_home(java_version)
puts “#{java_version} selected”
head -n-2 $HOME/.bash_profile > $HOME/.bash_profile.new
echo '#Modified by jdk_switcher\nJAVA_HOME=/usr/lib/jvm/#{java_version};export JAVA_HOME;PATH=$JAVA_HOME/bin:$PATH' >> $HOME/.bash_profile.new
mv $HOME/.bash_profile.new $HOME/.bash_profile
exec “/bin/bash -c ‘source ~/.bash_profile;’”
%x{/bin/bash -c “. ~/.bash_profile;”}
end

window = Gtk::Window.new
window.signal_connect(“delete_event”) {
false
}

window.signal_connect(“destroy”) {
Gtk.main_quit
}

option_menu = Gtk::OptionMenu.new
menu = Gtk::Menu.new
Dir.foreach("/usr/lib/jvm") do |e|
unless e[0,1] == “.”
mi = Gtk::MenuItem.new(e)
mi.signal_connect(“activate”) {
set_java_home(e)
}
menu.append(mi)
end
end

menu.show_all
option_menu.menu=menu
window.border_width = 10
window.add(option_menu)
window.show_all

Gtk.main

On Mon, 11 Jun 2007 12:56:19 +0900, Kevin J. wrote:

I actually got the behaviour I wanted by writing to .bashrc instead of
bash_profile, so now my workflow is:

  • run jdk_switcher -> open terminal -> run cli java app instead of:
  • vi .bashrc -> modify JAVA_HOME -> open new terminal -> run cli java
    app.

It’s a hell of a lot quicker than manually editing each time and I’m
fairly happy with the results and there’s no need for the source command
at all as the .bashrc is read every time I open a new terminal

Anyway, thanks for the help/suggestions

I don’t particulary like that, (it seems too hackish) but that’s just
me.
If there aren’t too many different configuration options consider
creating a shell function that does all of the reconfiguration
itself[1],
if there are too many options and this really demands a gui then have
your gui write to a separate configuration file, create a shell function
or alias that reloads that file (alias rejdk=“source
~/.jdk_config_file”)
and run that manually in your shell each time you update the config
file.

–Ken

[1] Option 1 would live in your .bashrc, and might look something like
this:

setjdk(){
case $1 in
gcj) export JAVA_HOME=… ;;
1.6) export JAVA_HOME=… ;;
1.5) export JAVA_HOME=… ;;
1.4) export JAVA_HOME=… ;;
kaffe) export JAVA_HOME=… ;;
esac
}

Hi,

If you were to run your Java program itself from ruby, I would think
that you could set the JAVA_HOME environment variable directly with
ENV[JAVA_HOME] = whatever you want, before executing Java as a
child process of the Ruby program.

I cannot run the Java code from the ruby program as I could be running
one of several apps.

I actually got the behaviour I wanted by writing to .bashrc instead of
.bash_profile, so now my workflow is:

  • run jdk_switcher -> open terminal -> run cli java app
    instead of:
  • vi .bashrc -> modify JAVA_HOME -> open new terminal -> run cli java
    app.

It’s a hell of a lot quicker than manually editing each time and I’m
fairly happy with the results and there’s no need for the source
command at all as the .bashrc is read every time I open a new terminal

Anyway, thanks for the help/suggestions

Kev

Hi,

I don’t particulary like that, (it seems too hackish) but that’s just me.

I tend to try to write the simplest thing that can work and go from
there. Right now it works, but constantly re-writing the .bashrc file
is suboptimal.

If there aren’t too many different configuration options consider
creating a shell function that does all of the reconfiguration itself[1],
if there are too many options and this really demands a gui then have
your gui write to a separate configuration file, create a shell function
or alias that reloads that file (alias rejdk=“source ~/.jdk_config_file”)
and run that manually in your shell each time you update the config file.

Yes that’s the next logical step to improve the code and when I have
time that’s what I will do.

Thanks,
Kev

On Jun 10, 2007, at 9:56 PM, Kevin J. wrote:

I actually got the behaviour I wanted by writing to .bashrc instead of
Anyway, thanks for the help/suggestions

Kev

kevin-

you can do all of this and more with session. the fundemental
difference with session is that it uses the SAME bash process for all
commands, therefore any environments commands, and even bash builtins
will all works. for example:

cfp:~ > cat a.rb
require ‘time’
require ‘rubygems’
require ‘session’ ### sudo gem install session

we’ll use this single bash instance for everthing

bash = Session::Bash.new

home = File.expand_path ‘~’

Dir.chdir home do
4.times do
### alter the .bashrc, setting an environment var
open(‘.bashrc’, ‘a+’) do |fd|
lines = fd.readlines
lines.delete_if{|line| line =~ %r/^export FOO=/}
lines << “export FOO=#{ Time.now.iso8601(2) }”
fd.write lines
end

 ### source it, now bash will pick up the environment var
 bash.execute 'source .bashrc'

 ### run a command that uses the environment, this could be java
 out, err = bash.execute 'echo "FOO: $FOO"'
 puts out

end
end

cfp:~ > ruby a.rb
FOO: 2007-06-14T08:27:22.90-06:00
FOO: 2007-06-14T08:27:22.93-06:00
FOO: 2007-06-14T08:27:22.96-06:00
FOO: 2007-06-14T08:27:22.98-06:00

-a

we can deny everything, except that we have the possibility of being
better. simply reflect on that.
h.h. the 14th dalai lama