I'm trying to write a script that will change directory (or set a variable), but after the script finishes, I'm back where I started (or my variable isn't set)!

Consider this:

   cd /tmp

If one executes this simple script, what happens? Bash forks, and the parent waits. The child executes the script, including the chdir(2) system call, and then exits. The parent, which was waiting for the child, harvests the child's exit status (presumably 0 for success), and then bash carries on with the next command.

Since the chdir was done by a child process, it has no effect on the parent.

Moreover, there is no conceivable way you can ever have a child process affect any part of the parent's environment, which includes its variables as well as its current working directory.

So, how does one go about it? You can still have the cd command in an external file, but you can't run it as a script. Instead, you must source it with . (or the Bash-only synonym, source).

   echo 'cd /tmp' > $HOME/mycd
   . $HOME/mycd
   pwd                          # Now, we're in /tmp

The same thing applies to setting variables. . ('dot') the file that contains the commands; don't try to run it.

Functions are run in the same same shell, so it is possible to put

   mycd() { cd /tmp; }

in .bashrc or similar, and then use mycd to change the directory.