date: 2026-02-22 · 2 min read
type: til · tags: #linux #shell #learning
TIL: Why cd in a Script Does Nothing
A script runs in a child process. Your shell does not care what the child did.
I wanted a shortcut to jump to a scratch directory:
mkdir -p /tmp/scratch && cd /tmp/scratch
My first thought was to put it in a script. That does not work.
The problem
A script runs in a child process — a separate shell spawned to execute the file. The cd happens inside that child. When the script finishes, the child exits, and you are right back where you started. Your shell never moved.
# scratch.sh
#!/bin/bash
mkdir -p /tmp/scratch && cd /tmp/scratch
$ pwd
/Users/todd
$ bash scratch.sh
$ pwd
/Users/todd # nothing changed
The same applies to environment variables. A script can set variables all day — your shell will never see them. The child process is disposable.
The fix: shell functions
A function runs in your current shell, not a child process. It can change your directory, set variables, and modify your environment.
Add this to ~/.zshrc:
scratch() { mkdir -p /tmp/scratch && cd /tmp/scratch; }
Now scratch drops you straight into /tmp/scratch. It works because the function executes in your shell, not a throwaway subprocess.
When to use what
| Need | Use | Why |
|---|---|---|
| Change directory | Function in .zshrc | Must run in current shell |
| Set environment variables | Function or source | Must affect current shell |
| Standalone task (build, deploy, lint) | Script in ~/bin/ or ~/.local/bin/ | Does not need to change shell state |
| Simple command shortcut | Alias in .zshrc | Shorter syntax for common commands |
The rule of thumb: if it needs to change the state of your shell, it cannot be a script. It has to be a function or alias loaded into the configuration of your shell.
Where these live
~/.zshrc— loads for every new interactive shell. Functions, aliases, and shell options go here.~/.zshenv— loads for every shell, including non-interactive ones (scripts, cron). Environment variables that everything needs go here.~/bin/or~/.local/bin/— directories on your PATH for standalone scripts. These run as child processes.
Another piece of Linux I was using without understanding. Now it makes sense why source .env works but bash .env does not change anything — same concept.