using ` and the bash source command

K

Kevin Jackson

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
 
V

vasudevram

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

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
http://www.dancingbison.com
 
R

Rick DeNatale

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.

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 exe$B!>(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?
 
K

Kevin Jackson

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 (c) 2007 Kevin Jackson
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
 
R

Rick DeNatale

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.
 
K

Kevin Jackson

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
 
K

Ken Bloom

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

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
}
 
K

Kevin Jackson

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
 
A

ara.t.howard

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

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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,230
Members
46,819
Latest member
masterdaster

Latest Threads

Top