This is my current way of doing it for bash (tested on Debian). Maybe there's a better way:
Don't do it with exec bash, for example like this:
#!/bin/bash
cd $1
exec bash
because while it appears to work, after
you run it and your script finishes, yes you'll be in the correct
directory, but you'll be in it in a subshell, which you can confirm by
pressing Ctrl+D afterwards, and you'll see it exits the subshell,
putting you back in your original directory.
This is usually not a state you want a script user to be left in after
the script they run returns, because it's non-obvious that they're in
a subshell and now they basically have two shells open when they
thought they only had one. They might continue using this subshell and not realize it, and it could have unintended consequences.
If you really want the script to exit and leave open a subshell in the
new directory, it's better if you change the PS1 variable so the
script user has a visual indicator that they still have a subshell
open.
Here's an example I came up with. It is two files, an outer.sh which you call directly,
and an inner.sh which is sourced inside the outer.sh script. The outer
script sets two variables, then sources the inner script, and
afterwards it echoes the two variables (the second one has just been
modified by the inner script). Afterwards it makes a temp copy of the
current user's ~/.bashrc file, adds an override for the PS1 variable
in it, as well as a cleanup routine, and finally it runs exec bash
--rcfile pointing at the .bashrc.tmp file to initialize bash with a modified environment, including the modified prompt and the cleanup
routine.
After outer.sh exits, you'll be left inside a subshell in the desired directory (in this case testdir/ which was entered into by the inner.sh script) with a visual
indicator making it clear to you, and if you exit out of the subshell,
the .bashrc.tmp file will be deleted by the cleanup routine, and you'll be back in the directory you started in.
Maybe there's a smarter way to do it, but that's the best way I could
figure out in about 40 minutes of experimenting:
file 1: outer.sh
#!/bin/bash
var1="hello"
var2="world"
source inner.sh
echo $var1
echo $var2
cp ~/.bashrc .bashrc.tmp
echo 'export PS1="(subshell) $PS1"' >> .bashrc.tmp
cat <<EOS >> .bashrc.tmp
cleanup() {
echo "cleaning up..."
rm .bashrc.tmp
}
trap 'cleanup' 0
EOS
exec bash --rcfile .bashrc.tmp
file 2: inner.sh
cd testdir
var2="bird"
then run:
$ mkdir testdir
$ chmod 755 outer.sh
$ ./outer.sh
it should output:
hello
bird
and then drop you into your subshell using exec bash, but with a
modified prompt which makes that obvious, something like:
(subshell) user@computername:~/testdir$
and if you Ctrl-D out of the subshell, it should clean up by deleting
a temporary .bashrc.tmp file in the testdir/ directory
I wonder if there's a better way than having to copy the .bashrc file
like that though to change the PS1 var properly in the subshell...