What is the best way to execute a script when entering into a directory?
When I move into a new directory I would like bash to execute the projectSettings.bash script much like RVM does.
4 Answers
You can make cd a function (and pop and pushd), and make it detect if you enter that particular directory.
cd () { builtin cd "$@" && chpwd; }
pushd () { builtin pushd "$@" && chpwd; }
popd () { builtin popd "$@" && chpwd; }
unset_all_project_settings () {
# do whatever it takes to undo the effect of projectSettings.bash,
# e.g. unset variables, remove PATH elements, etc.
}
chpwd () {
case $PWD in
/some/directory|/some/other/directory) . ./projectSettings.bash;;
*) unset_all_project_settings;;
esac
}
Do not do this in directories that you haven't whitelisted, because it would make it very easy for someone to trick you into running arbitrary code — send you an archive, so you unzip it, change into the directory it created, and you've now run the attacker's code.
I don't recommend this approach, because it means the script will be executed even if you enter that directory for some reason that's unrelated to working on the project. I suggest having a specific function that changes to the project directory and sources the settings script.
myproj () {
cd /some/directory && . ./projectSettings.bash
}
-
1I only started in Ruby a little while ago. The RVM tool tho is completely in Bash and one of the best pieces of Bash magic I have seen. I think the answer is a little silly because one of the absolutely worse things you can ever do is over ride something like
cdand there is with out doubt a better way. Even using $PROMPT_COMMAND is better!James– James2011-10-02 23:21:29 +00:00Commented Oct 2, 2011 at 23:21 -
6I was completely wrong and apologize. RVM was overloading cd.James– James2011-10-07 14:22:21 +00:00Commented Oct 7, 2011 at 14:22
-
5(removed some tangential pro/anti-Ruby stuff from this comment thread)Michael Mrozek– Michael Mrozek2012-07-25 21:04:20 +00:00Commented Jul 25, 2012 at 21:04
-
2in the projectSettings.bash I suggest you to add a flag variable to not repeat the initialization in case you exit/re-enter the directory. So enclose everything in
if [ -z $MYSETTINGS ] ; then export MYSETTINGS=1 ; echo your settings here ; fi. This is to avoid problems in case you do something PATH=/mytools/bin:$PATH kind of initialisation.spider– spider2015-09-18 09:57:45 +00:00Commented Sep 18, 2015 at 9:57 -
5@spider Rather there should be some kind of unset mechanism if you leave the directory. If you leave and reenter, you should get the settings back!Gilles 'SO- stop being evil'– Gilles 'SO- stop being evil'2015-09-18 10:11:20 +00:00Commented Sep 18, 2015 at 10:11
direnv might be what you are looking for.
direnvis an extension for your shell. It augments existing shells with a new feature that can load and unload environment variables depending on the current directory.
Here is an example taken from the official documentation:
$ cd ~/my_project
$ echo ${FOO-nope}
nope
$ echo export FOO=foo > .envrc
.envrc is not allowed
$ direnv allow .
direnv: reloading
direnv: loading .envrc
direnv export: +FOO
$ echo ${FOO-nope}
foo
$ cd ..
direnv: unloading
direnv export: ~PATH
$ echo ${FOO-nope}
nope
It's my cd function
function cd()
{
if [ -f .exit.sh ]; then
source .exit.sh;
fi
if [ -z $* ]; then
builtin cd ~
else
builtin cd "$*"
fi
if [ -f .enter.sh ]; then
source .enter.sh;
fi
}
And then you can write your script in ".enter.sh" or ".exit.sh".
-
1Replace the whle middle
if [ -z $* ](which breaks if you pass cd more than 1 argument) with one line:builtin cd "$@"glenn jackman– glenn jackman2023-10-19 15:32:24 +00:00Commented Oct 19, 2023 at 15:32 -
With this approach: any evil user can put harmful scripts in some path (ex: inside /tmp ? ) and you will execute whatever command there is inside those harmfull scripts whenever you cd into (or cd from) those path... It is incredibly dangerous.Olivier Dulac– Olivier Dulac2023-11-29 13:19:58 +00:00Commented Nov 29, 2023 at 13:19
ondir is another alternative to run scripts as you enter directories in a terminal.
.envrcfiles: direnv.net