exec — Replace the current shell with another program across all 5 shells
Equivalents in every shell
exec python script.pyBuiltin. REPLACES the current shell process with `python` — no fork. After `exec`, control NEVER returns to the script. Common uses: pinning a wrapper to its target (`exec real_program "$@"`), or redirecting file descriptors for the rest of the script (`exec 2>logfile`).
exec python script.pySame as bash. Zsh also supports `exec -c` (clear environment) and `exec -l` (login-shell-style argv[0] — first char `-`).
exec python script.pyFish builtin. Same process-replacement semantics — the fish process is gone after `exec`, replaced in place by the new program with the same PID and file descriptors.
& python script.pyPowerShell has NO `exec` (process replacement). The call operator `&` SPAWNS a child. The closest mental model is `Invoke-Command` for remoting, but no native in-place exec — the pwsh process stays alive throughout.
cmd /c python script.pyCmd has no process-replacement. `cmd /c` spawns a child that runs the program, then returns to your shell. There is no way to replace the cmd.exe parent in place — the process tree always retains the launching cmd.
Worked examples
Replace the shell with another program
exec python -u long_run.pyexec python -u long_run.pyexec python -u long_run.pypython -u long_run.py; exitRedirect all subsequent script output to a logfile
exec > script.log 2>&1exec > script.log 2>&1exec > script.log 2>&1Open a numbered file descriptor for repeated reads
exec 3< input.txt; read -u 3 line; read -u 3 line2exec 3< input.txt; read -u 3 line; read -u 3 line2Gotchas
- `exec` REPLACES the current process — anything AFTER `exec` in the script NEVER runs. Putting commands after `exec PROGRAM` is almost always a bug. The exception is `exec` used WITHOUT a command (for FD redirection only), which keeps the shell alive.
- `exec` with NO command but WITH redirections (`exec > log.txt`) is the "from now on, write all output to this file" idiom. It DOES NOT replace the process — it just permanently changes the file descriptors of the current shell for the remainder of the script.
- Inside a subshell `( exec foo )`, only the SUBSHELL is replaced. The parent shell continues normally. This pattern is sometimes used to scope `exec` redirections without polluting the rest of the script — confusing if you don't know about subshell isolation.
- PowerShell `& python script.py` SPAWNS python as a child of pwsh. Exit code propagation requires `$LASTEXITCODE`. There is no way to make pwsh "become" python — porting bash scripts that rely on `exec` for memory/PID equivalence is fundamentally impossible.
- Cmd `start /b /wait` is the closest cmd analogue but still SPAWNS a child. The cmd process never goes away during execution — wrappers that `exec` in bash to avoid an extra cmd.exe in the process tree have no Windows-side equivalent.