LinuxQuestions.org
Help answer threads with 0 replies.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Linux Forums > Linux - General
User Name
Password
Linux - General This Linux forum is for general Linux questions and discussion.
If it is Linux Related and doesn't seem to fit in any other forum then this is the place.

Notices


Reply
  Search this Thread
Old 06-08-2007, 10:30 AM   #1
hexalite2k5
LQ Newbie
 
Registered: Jun 2007
Posts: 3

Rep: Reputation: 0
Bash configuration files


Hi

I have the following problem and i cannot seem to find a way to solve it.
My computer usually has a lot of tasks running and is not uncommon to have
around 20 different bash consoles running.
At some point i need to set a new environment variable.
Sure, this value will be available for any new console , but i need it to be also available
for the currently running bash instances.

The only solution i know:
modify .bashrc and then source .bashrc takes a lot of time for 20 consoles.

Is there a way to do this rapidly and efficiently.

Many thanks.
 
Old 06-08-2007, 12:19 PM   #2
trickykid
LQ Guru
 
Registered: Jan 2001
Posts: 24,149

Rep: Reputation: 269Reputation: 269Reputation: 269
There's really no other way. My suggestion would be to write a script that sources the .bashrc file every couple of minutes and have it run in the background for each console.
 
Old 06-08-2007, 01:41 PM   #3
dawkcid
Member
 
Registered: May 2007
Location: UK
Distribution: LFS,Slackware,Slamd64,NetBSD
Posts: 102

Rep: Reputation: 15
Assuming your editor makes backups, what about

diff ~/.bashrc~ ~/.bashrc > /tmp/bashrc-new

Then

source /tmp/bashrc-new

in each shell. You could easily write a wrapper to handle the editing+diff.

Alternatively, if it's just environment variables that you are changing, you could keep your environment variables in a seperate file and source it in ~/.bashrc. Then you just need to edit ~/.bashrc-env (or whatever) and source that in each shell, which would avoid re-running the whole of bashrc. I use seperate files for functions, aliases, key bindings (I use zsh), etc.

Last edited by dawkcid; 06-08-2007 at 01:43 PM.
 
Old 06-09-2007, 11:15 AM   #4
dawkcid
Member
 
Registered: May 2007
Location: UK
Distribution: LFS,Slackware,Slamd64,NetBSD
Posts: 102

Rep: Reputation: 15
Uh, my above post is wrong, of course, it should be piped through sed. I must
have cut-n-pasted the wrong line. Ahem.

diff ... | sed -n 's/^> \(.*\)$/\1/p' > ~/.bashrc-update

(Although, a unified diff might be better.)

However, it may or may not be worth doing this, depending on how long and/or
complex your ~/.bashrc is and how many shells you have running.

One important point, of course, is that because diff is line-based, you can
only use this method for single line changes. If you change, say, the contents
of a for loop, then obviously it's not going to work.

But if you keep your aliases and environment variables in a seperate file,
this could save some time if, say, you are only adding/modifying a single
variable.

As for getting the shells to reload the config file(s), the best way to do
this is to use a signal, say SIGUSR1, to automatically reload the file.

e.g. in ~/.bashrc, you put something like:

Code:
trap '. ~/.bashrc' USR1
you can then killall -USR1 bash. This way, you don't need to periodically
check for updates, you just signal every shell when you make changes.

Naturally, you'd use a shell wrapper to combine these steps:

Code:
#!/bin/sh
$EDITOR ~/.bashrc
killall -USR1 bash
You could also check the mtime of ~/.bashrc, so if you changed your mind and
exited without modifying anything, it wouldn't cause an unnecessary update:

Code:
#!/bin/sh
mtime=$(stat -c %Y ~/.bashrc)
$EDITOR ~/.bashrc
if [[ $(stat -c %Y ~/.bashrc) > $mtime ]]; then
        killall -USR1 bash
fi
To imporove efficiency, you could split ~/.bashrc into seperate files as I
suggested before. You might use files like ~/.bash/alias, ~/.bash/env, etc
(you can call the files whatever you want, of course, such as ~/.bash-aliases
but using a subdirectory reduces the clutter in $HOME).

E.g. bash-update.sh:

Code:
#!/bin/sh
files=() 
bashrc=1 

if [[ -z $1 ]]; then
        files=(~/.bash/alias ~/.bash/env)
else           
        for f in $@; do
                case $f in
                        (alias|env) files=(${files[*]} ~/.bash/"$f"); bashrc="";;
                        (rc|bashrc) files=(); bashrc=1;;
                esac
        done
fi          
            
$EDITOR ${files[*]}

# There are any number of ways to tell bash which files have changed, we'll
# keep it simple:
rm -f ~/.bash/.update

# If you edit ~/.bashrc, that will included the alias and env files anyway, so
# we don't add them to the update file, even if the've changed, otherwise we'd
# end up reading them twice.
if [[ -n $bashrc ]]; then   
        echo ~/.bashrc >> ~/.bash/.update
else
        for f in ${files[*]}; do
                echo $f >> ~/.bash/.update
        done
fi
  
killall -USR1 bash
Now, we need to update the trap function in ~/.bashrc:

Code:
reload-config()
{
        if [[ -f ~/.bash/.update ]]; then
                while read file; do
                        . $file
                done < ~/.bash/.update
        fi
}
 
trap reload-config USR1
Now you can run the script naming whichever file or files you need to edit. If
you don't specify any files, it will edit all of them.

bash-update [rc|alias|env]

Thus, when you exit the editor, all running shells will be updated
automatically, and will only re-read files that have been edited (though if
you changed ~/.bashrc, that will obviously read in all the other files too).

You could extend this to include the mtime check.

Once thing to note is that the ~/.bash/.update file gets left lying around
afterwards. It get overwritten when you make new changes, so it shouldn't
cause a problem, but obviously isn't ideal. The problem is that you can't just
delete it any old time, because obviously every shell has to include it before
it can be deleted, and you don't know when all the shells have finished
reading it. One solution is to use reference counts on the file (i.e. hard
links). For each running shell, create a hard link to ~/.bash/.update-$pid and
rm that link after that shell has included the file.

To incorporate this, change the reload function to:

Code:
reload-config()
{
        if [[ -f ~/.bash/.update-$$ ]]; then
                while read file; do
                        . $file
                done < ~/.bash/.update-$$
                rm ~/.bash/.update-$$
        fi
}
and extend the script:

Code:
#!/bin/sh
files=() 
bashrc=1 

for f in $@; do
        case $f in
                (alias|env) files=(${files[*]} ~/.bash/"$f"); bashrc="";;
                (rc|bashrc) files=(~/.bashrc); bashrc=1;;
        esac
done

if [[ -z $files ]]; then
        files=(~/.bashrc ~/.bash/alias ~/.bash/env)
fi
  
$EDITOR ${files[*]}

# There are any number of ways to tell bash which files have changed, we'll
# keep it simple:
rm -f ~/.bash/.update

# If you edit ~/.bashrc, that will included the alias and env files anyway, so
# we don't add them to the update file, even if the've changed, otherwise we'd
# end up reading them twice.
if [[ -n $bashrc ]]; then   
        echo ~/.bashrc >> ~/.bash/.update
else
        for f in ${files[*]}; do
                echo $f >> ~/.bash/.update
        done
fi
  
pidlist=($(ps ax -o pid,comm | grep bash | awk '{print $1}'))
for p in ${pidlist[*]}; do
        ln -f ~/.bash/.update ~/.bash/.update-$p
done

killall -USR1 bash
rm ~/.bash/.update
If you have a lot of aliases and/or environment variables, you could also
incorporate the diff technique only for the alias and env files (this time
~/.bash/.update contains code that we eval, rather than a list of files).

Obviously, you must keep each assignment on a single line. If you need
different aliases for different systems, e.g.

Code:
sys=$(uname -s)
if [[ $sys == Linux ]]; then
        alias so_and_so='whatever -some_opt'
elif [[ $sys == Solaris ]]; then
        alias so_and_so='whatever -different_opt'
fi
then you must put that in ~/.bashrc or some other file (or make sure never to
modify it using the update script).

But, providing that everything in those files is line-based, this should work:
Code:
#!/bin/sh
files=() 
bashrc=~/.bashrc

update_dir=~/.bash/.update.d

mkdir -p $update_dir

if [[ -z $1 ]]; then
        files=(~/.bash/alias ~/.bash/env)
else
        for f in $@; do
                case $f in
                        (alias|env) files=(${files[*]} ~/.bash/"$f"); bashrc="";;
                        (rc|bashrc) files=(); bashrc=~/.bashrc;;
                esac
        done
fi
  
rcmtime=$(stat -c %Y ~/.bashrc)
mtime=()

for f in ${files[*]}; do
        mtime=(${mtime[*]} $(stat -c %Y $f))
        cp $f $update_dir/${f##*/}-tmp-$$   
done

$EDITOR $bashrc ${files[*]}

# There are any number of ways to tell bash which files have changed, we'll
# keep it simple:
rm -f ~/.bash/.update

# If you edit ~/.bashrc, that will included the alias and env files anyway, so
# we don't add them to the update file, even if the've changed, otherwise we'd
# end up reading them twice.
if [[ -n $bashrc ]]; then   
        if [[ $(stat -c %Y ~/.bashrc) > $rcmtime ]]; then
                echo 'source ~/.bashrc' >> ~/.bash/.update
        fi
else
        for i in $(seq 0 $(( ${#files[*]} - 1)) ); do
                if [[ $(stat -c %Y ${files[$i]}) > ${mtime[$i]} ]]; then
                        diff -u $update_dir/${f##*/}-tmp-$$ $f | sed -n 's/^+\([^+].*\)$/\1/p' >> ~/.bash/.update
                fi
        done
fi
  
if [[ -f ~/.bash/.update && $(stat -c %s ~/.bash/.update) > 0 ]]; then
        pidlist=($(ps ax -o pid,comm | grep bash | awk '{print $1}')) 
        for p in ${pidlist[*]}; do
                ln -f ~/.bash/.update ~/.bash/.update-$p
        done

        echo UPDATE
        cat ~/.bash/.update

        killall -USR1 bash
fi
  
rm -fr ~/.bash/.update $update_dir
which requires a modified reload-config function:

Code:
reload-config()
{
        if [[ -f ~/.bash/.update-$$ ]]; then
                . ~/.bash/.update-$$
                rm ~/.bash/.update-$$
        fi
}
Decide for yourself if any of these additional schemes are worth the overhead
compared to a simple edit and blanket re-source. If you have a _lot_ of shells
running (I have over 100...), then sourcing only the modified stuff should
be more efficient, maybe a lot more, especially if your rc file runs a lot of
external commands.
 
Old 06-11-2007, 05:43 AM   #5
hexalite2k5
LQ Newbie
 
Registered: Jun 2007
Posts: 3

Original Poster
Rep: Reputation: 0
Hi

I have just returned and can't wait to read what you wrote.
thanks a lot guys
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
where ubuntu stores bash configuration? santana Ubuntu 2 04-25-2007 02:31 PM
bash configuration files abbynormal Linux - Newbie 4 06-15-2005 05:13 PM
GRUB bash-like interface configuration trouble vishwanath_79 Linux - Laptop and Netbook 5 03-08-2005 02:00 AM
making .tif files into animated .gif files (bash shell, Red Hat 7.2) illiniguy3043 Linux - Newbie 1 06-01-2004 04:04 PM
Where are programs files and configuration files stored? nutshell Linux - General 2 03-09-2002 10:24 AM

LinuxQuestions.org > Forums > Linux Forums > Linux - General

All times are GMT -5. The time now is 06:15 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration