3

Does a running shell have a mechanism to recreate its invocation?

I sometimes make changes to variables or my profile and have to manually source the startup files to make things run again, for example, the PATH.

This would mostly apply to interactive shells, I suppose.

I use bash 4 in OS X, but the question is not restricted to these.

1.61803
  • 1,241

1 Answers1

3

I do exec bash. It cleans up variables and functions. It shouldn't clean up newly opened file descriptors (with exec $someNumber<>somefile ) or exported variables, but I rarely open new files with exec and my startup files (which get loaded when you do exec bash) restore environment variables I care about (such as PATH) to the state I want them in.

( Doing a plain bash has the disadvantage that it'll stick another shell to your current one so then you'll have to exit one additional time. )

If I don't care about cleaning up extra variables and functions, I just re-load the dot files.

I edit ~/.bashrc and reload it fairly often so I have a keyboard shortuct for the combo:

bashrcEdit() { $EDITOR "$HOME/.bashrc"; }
bind -x '"\C-e": bashrcEdit && bashrcReload'

and for plain reloading:

bind -x '"\C-o": bashrcReload;'

( My reload function is:

bashrcReload() { unset bashrcSourced; unset PROFILE_SOURCED; source "$HOME/.bashrc"; }

instead of just:

source "$HOME/.bashrc";

It has the unsets because those variables (first nonexported—for .bashrc, second exported—for .profile) serve as double include guards in my setup–without those unsets, sourceing .bashrc would be a no-op in my setup. )


Doing it right

If you want to be thorough, you can

  • snapshot the environment at the end of your .bashrc (and then reload it--this you can do perfectly)
  • snapshot which filedescriptors are open (and then close all others--this isn't and never can be a perfect solution)

Add this at the end of your .bashrc:

#Take a snapshot of the env and fds
envSnap() { 
   export FDS="$(/bin/ls /proc/$$/fd/)"
   export ENV="$(export -p)"
}
#Restore env, close extra fds, and re-exec bash
envRestore(){
  exec env - ENV="$ENV" bash --noprofile -c '
    eval "$ENV"
    for fd in `{ /bin/ls /proc/$$/fd; echo "$FDS"; } | sort | uniq -u`; do
       eval "exec $fd>&-"
    done
    exec bash
   '
}
#Take the snapshot now
envSnap

Now you can do envRestore to pretty much perfectly restore the environment.

E.g., if you do:

export FOO=bar
touch file
exec 5<file

Then after an exec bash, you'll still have: /bin/ls /proc/self/fd include 5, and echo $FOO print bar.

If you do envRestore instead of exec bash, however, then /bin/ls/ /proc/self/fd shouldn't include 5 (unless your bash usually starts with fd 5 open) and echo $FOO should print an empty line.


(File descriptors are a weak point of this, because closing the extra filedescriptors doesn't guarantee that the original file descriptors weren't redirected or that the vtable entries they point at haven't been changed in a way that will change how reads or writes on that filedescriptor will behave.)

Petr Skocik
  • 28,816
  • I already have an alias in place to . /etc/profile && . ~/.profile, but it didn't come to mind exec. So for now, it would be a combination of exec, source and unset. Any other additions? – 1.61803 Nov 20 '15 at 19:56
  • You could take a snapshot of your environment and your opened fileDeskriptors and then restore those accordingly if you wanted to be perfectly thorough. Would need to patch env (or make an env dumper from scratch) so that the output is shell eval'lable. – Petr Skocik Nov 20 '15 at 20:10
  • @1.61803 Added info on how to do it if you really wanted to do it correctly. – Petr Skocik Nov 20 '15 at 21:11
  • Thanks for the thoroughness. Albeit, I was searching for a more simple mechanism of self-cleanup — there seems to be none. – 1.61803 Nov 21 '15 at 14:57
  • 1
    @mikeserv Damn, didn't know about that one. That's a nice replacement for the envSnapshot custom-compiled executable! Although now that I wrote the c file, I'm kind of emotionally attached to it. – Petr Skocik Nov 21 '15 at 15:59
  • @mikeserv Replaced the custom environment snapshotter with export -p, thanks mikeserv! – Petr Skocik Nov 21 '15 at 16:06
  • 1
    in most things you can recreate state exactly as you found it - excepting fds (as you say) and shell functions (portably). otherwise, local vars can be had w/ set, aliases w/ alias, environment (to the extent the shell can reliably duplicate it at all), export -p, readonly -p for read only variables. in bash you can get functions as well w/ declare -fp. – mikeserv Nov 21 '15 at 16:13