Use a subshell to avoid having to `cd` back

Wrap a temporary `cd` in `( ... )` so the parent shell keeps its directory.

Problem

Long scripts that `cd somewhere` and forget to `cd -` end up running later commands in unexpected directories. Subshells give you a scoped working directory at no readability cost.

Bad

cd /tmp/build
make
cd ..               # easy to forget; also fails silently
do_more_stuff

Good

(
  cd /tmp/build || exit
  make
)
do_more_stuff       # still in the original directory

Explanation

Anything inside `( ... )` runs in a child process. The child's `cd` cannot affect the parent's working directory, so the parent automatically returns to where it was.

Related

SC2164

When It Matters

cd into a directory, do work, cd .. back is fragile: if the work fails and the script uses set -e, or if an early return or exit fires, the cd back never runs and everything afterwards executes in the wrong directory. With relative paths and a destructive command, that is how scripts delete the wrong tree. cd .. is also wrong whenever the target was reached through a symlink or the code path could have descended more than one level, and it silently breaks if someone later changes the directory being entered.

Second Example

Note

Many tools accept a directory argument — make -C, git -C, tar -C — which is better still because nothing about the shell state changes.

Exceptions

A single unconditional cd at the top of a script that is meant to run entirely inside one directory is fine and idiomatic: cd "$(dirname "$0")" || exit 1. The warning is about the cd X; work; cd .. round trip, not about changing directory in general.

Faq

Q

Does a subshell slow the script down?

A

It forks once, which is negligible compared to almost any command you would run inside it, and far cheaper than debugging a script that ended up in the wrong directory.

Q

Do variables set inside the subshell survive?

A

No. Assignments inside ( ) are discarded when it exits. If you need a value out, echo it and capture the output, or restructure using pushd/popd.

Q

What about pushd and popd?

A

They work in Bash and keep a directory stack, but they are not POSIX and popd is skipped just as easily as cd .. when an error aborts the block.