Skip to content

cd

cdchange directory)是唯一一个能让你在文件系统里自由穿梭的命令——但它也是 shell 脚本中最容易踩坑的命令:没有外部二进制,全部由 shell 内建实现,子 shell 里的 cd 对外面毫无影响。

速查卡片

需求命令
回到家目录cdcd ~
回到上一个目录cd -
进入指定目录cd /path/to/dir
进入上级目录cd ..
进入用户家目录cd ~username
物理模式(解析符号链接)cd -P /path
逻辑模式(保留符号链接)cd -L /path
全局物理模式set -P 写入 ~/.bashrc
脚本中安全 cdcd /path || exit 1
不输入 cd 也能切换shopt -s autocd(bash)

概述

cd 是 shell 内建命令——不存在 /bin/cd 这样的外部二进制。这个事实有两个深远后果:

  1. shell 脚本中 cd 只在子 shell 内生效,脚本退出后父 shell 工作目录不变
  2. cd 的所有行为细节——-L/-P$CDPATH$OLDPWD、错误处理——全部由 shell 自身实现,不同 shell 可能有细微差异

cd 也是 POSIX 规定最详尽的命令之一:规范用整整 10 个步骤精确描述了它的内部算法。

命令签名

bash
# POSIX 标准签名
cd [-L | -P] [directory]
cd -

# bash 完整签名
cd [-L] [-@] [dir]
cd -P [-e] [-@] [dir]

选项

选项适用模式说明
-LPOSIX逻辑模式——先处理 .. 再解析符号链接(默认
-PPOSIX物理模式——先解析符号链接再处理 ..
-e仅 bash,需配合 -P若切换成功后无法确定新目录的物理路径,返回非零退出码
-@仅 bash将文件扩展属性作为目录打开(极罕见)

同时指定 -L-P 时,最后一个生效。POSIX 规定不指定选项时等效 -L

基础用法

示例一:最简——无参数

bash
$ pwd
/tmp/some/deep/path
$ cd
$ pwd
/home/zolin

不带参数时等价于 cd "$HOME"。如果 $HOME 未设置或为空则报错。

示例二:绝对路径和相对路径

bash
$ cd /etc/nginx               # 绝对路径:一个 / 直达
$ pwd
/etc/nginx

$ cd sites-available          # 相对路径:从当前目录出发
$ pwd
/etc/nginx/sites-available

$ cd ../sites-enabled         # 相对路径 + .. 组合
$ pwd
/etc/nginx/sites-enabled

示例三:cd - —— 在两个目录间来回

bash
$ cd /tmp
$ cd /etc
$ cd -
/tmp                           # 打印切换后的路径
$ cd -
/etc

cd - 等价于 cd "$OLDPWD"。shell 启动后 $OLDPWD 为空,首次 cd - 会报错 cd: OLDPWD not set

cd - 只能回到最近一个目录,不存在"更早的历史"。zsh 有 cd -2 等扩展,bash 需借助 pushd/popd 或第三方工具。

示例四:符号链接下 cd .. 的分歧

bash
# 准备
$ mkdir -p /tmp/real/sub
$ ln -s /tmp/real /tmp/link

# 通过符号链接进入
$ cd /tmp/link/sub

# 默认 -L 模式下的 cd ..
$ cd .. && pwd
/tmp/link                     # 沿逻辑路径返回

# -P 模式下的 cd ..
$ cd /tmp/link/sub
$ cd -P .. && pwd
/tmp/real                     # 沿物理路径返回

这就是 "cd .. 回不到上一级" 困惑的根因。它回到的是"你走过的路"的上一步,而不是真实文件系统的父目录。

-L vs -P:符号链接处理的完整对照

-L-P 的区别不仅影响当前目录,还影响路径中 .. 分量的解析顺序:

-L(逻辑,默认)-P(物理)
符号链接保留,不解析全部解析为真实路径
.. 的处理时机先按逻辑路径去掉上一级分量,再解析符号链接先解析路径中的符号链接得到真实路径,再去掉上一级
$PWD 更新值逻辑路径(可能含符号链接分量)pwd -P 的结果
典型场景交互使用,保留导航路径的人性化脚本中使用,确保路径判断基于真实文件系统

全局切换默认行为

bash
# bash:使 cd 和 pwd 默认使用物理模式
set -P
set -o physical

# 恢复默认
set +P
set +o physical

# 写入 ~/.bashrc 永久生效
echo 'set -P' >> ~/.bashrc

# zsh 等效
set -w
set -o chaselinks

$CDPATH —— cd 的搜索路径

$CDPATHcd 最被低估的特性,类似于 $PATH 对可执行文件的搜索机制:

bash
# 设置 CDPATH
export CDPATH=".:/home/zolin/projects:/var/www"
规则说明
触发条件参数不以 / 开头,且第一分量不是 ...
分隔符冒号 :
空分量表示当前目录(:: 或开头/结尾的 :
匹配后行为cd打印实际切换到的路径
bash
$ CDPATH=.:/var
$ cd www
/var/www               # 打印了实际路径(不易察觉的影响)

$ pwd
/var/www

⚠️ CDPATH 的打印行为可能干扰脚本输出。如果依赖 cd 的安静输出,考虑在脚本中取消 CDPATHunset CDPATH

$HOME$OLDPWD

变量作用设置时机
$HOMEcd 无参数时的默认目标;~ 展开的基础用户登录时设置,通常不变
$OLDPWD上一个工作目录;cd - 的跳转目标每次 cd 成功时自动更新
bash
$ echo $OLDPWD          # shell 刚启动时为空

$ cd /tmp
$ cd /var
$ echo $OLDPWD
/tmp

$ cd -
/tmp
$ echo $OLDPWD
/var                    # 注意:又变了

bash 扩展选项(shopt)

bash 通过 shopt 提供了几个提升交互体验的 cd 相关选项:

shopt 选项作用启用
autocd直接输入目录名即等同于 cd,省去键盘输入shopt -s autocd
cdable_varscd 参数不是目录时,尝试将其作为变量名取值shopt -s cdable_vars
cdspell自动修正目录名中的小拼写错误shopt -s cdspell
direxpandTab 补全时将 $HOME 展开为绝对路径shopt -s direxpand
bash
# autocd 示例
$ shopt -s autocd
$ /tmp                 # 无需输入 cd,直接回车即切换
$ pwd
/tmp

# cdable_vars 示例
$ shopt -s cdable_vars
$ proj=/home/zolin/projects
$ cd proj              # 将 proj 当作变量名,取其值
$ pwd
/home/zolin/projects

POSIX 规定的 cd 内部算法

POSIX 用 10 步精确描述了 cd 的工作流程(curpath 为中间变量):

1. 无参数且 $HOME 为空 → 实现定义(bash 报错)
2. 无参数且 $HOME 非空 → curpath = $HOME
3. 参数以 / 开头 → curpath = 参数,跳到步骤 7
4. 第一分量为 . 或 .. → 跳到步骤 6
5. 遍历 $CDPATH 搜索,找到匹配目录则 curpath = 匹配路径
6. curpath = 参数(相对路径,后续转绝对)
7. -P 模式 → 跳到步骤 10
8. 基于 $PWD 构建完整绝对路径
9. 规范化(消解 . / ..,但不解析符号链接在 -L 下的含义)
10. 处理 PATH_MAX 超长
11. 执行 chdir() 系统调用

这个算法揭示了为什么 cd 在符号链接下的行为如此微妙——步骤 8 在 -L 模式下"基于 $PWD 构建路径",而 $PWD 可能就是逻辑路径。

脚本中的 cd

核心大坑:子 shell 隔离

bash
#!/bin/bash
# myscript.sh
cd /tmp
pwd             # /tmp
bash
$ pwd
/home/zolin
$ ./myscript.sh
/tmp             # 脚本内部输出
$ pwd
/home/zolin      # 父 shell 不受影响!

每个脚本在独立的子 shell 中运行,cd 只改变子 shell 的工作目录。脚本退出后子 shell 销毁,什么都留不下。

要在当前 shell 中生效,必须用 source(或 .):

bash
$ source ./myscript.sh      # 或:. ./myscript.sh
$ pwd
/tmp                        # 这次生效了

安全模式:cd || exit

ShellCheck 规则 SC2164——如果 cd 失败,后续命令会在错误目录中执行,可能造成灾难:

bash
# ❌ 危险:cd 失败后 rm 跑在错误目录
cd /nonexistent/path
rm -rf *                     # 在当前位置删东西!

# ✅ 安全
cd /nonexistent/path || exit 1

# ✅ 函数中
myfunc() {
    cd "$1" || return 1
    # ... 后续操作 ...
}

# ✅ 全局保护
set -e                       # 任何命令失败即退出
cd /some/path                # cd 失败自动终止

获取脚本所在目录

bash
# 经典写法
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"

# 符号链接安全版(需 readlink -f 或 realpath)
SCRIPT_DIR="$(cd "$(dirname "$(readlink -f "$0")")" && pwd -P)"

# 存储后跳回
HERE="$PWD"
cd /some/other/place || exit
# ... 工作 ...
cd "$HERE" || exit

权限:进入目录需要什么

权限能否 cd 进入能否 ls 浏览
只有 r❌(基本也不行)
只有 x❌(能看到文件,但列不了表)
r + x
都没有

进入目录只需要执行权限(x,这也是为什么它被称为"搜索权限"——你需要"搜索"通过这个目录才能到达它内部。

常见报错

错误信息原因解决方法
cd: too many arguments目录名含空格未加引号cd "my dir"cd my\ dir
cd: no such file or directory路径不存在或相对路径依赖了错误的工作目录确认路径拼写;用绝对路径

| cd: permission denied | 缺少目录执行权限 x | chmod +x dirsudo | | cd: not a directory | 目标是文件而非目录 | 检查路径,用 ls -l 确认 | | cd: Too many levels of symbolic links | 符号链接循环或深度超过 40 层 | ls -l 检查链接链,修复循环 |

| cd: OLDPWD not set | shell 启动后尚未执行过 cd 就用了 cd - | 先正常 cd 一次 |

相关命令与变量

命令/变量关系
$HOMEcd 无参数的默认目标
$OLDPWD上一个目录,cd - 的跳转目标
$PWD当前目录,cd 成功后自动更新
$CDPATHcd 的相对路径搜索列表
$DIRSTACK(bash)目录栈数组(pushd/popd 操作的底层变量)
pwd确认 cd 切换结果
pushd / popd / dirs目录栈操作

| chdir() | cd 的底层系统调用 | | chmod | 修改目录权限 |

常见误区

❌ 误区✅ 正解
"脚本中的 cd 会改变我的工作目录"脚本在子 shell 运行,cd 效果不传递到父 shell——用 source 执行才行
"cd 有可执行文件 /bin/cd"cd 是纯内建命令,没有外部二进制——否则它根本没法改变 shell 的工作目录
"cd .. 总是回到物理父目录"默认 -L 模式沿逻辑路径——通过符号链接进入时可能不是你预期的目录
"cd - 可以回溯多次"bash 中只能在最近两个目录间切换;zsh 支持 cd -2/cd +3 等多级回溯
"无执行权限也能进目录看文件"目录的执行权限 x 是进入的前提——没有 x,即使有读权限也进不去

zsh 差异

特性bashzsh
cd -N(回退 N 步)
cd +N(目录栈索引跳转)
autocdshopt -s autocdsetopt auto_cd
物理模式set -Pset -o chaselinks

macOS 特别说明

  • macOS 默认 shell 为 zsh,cd 为 zsh 内建
  • macOS 文件系统(APFS/HFS+)默认不区分大小写,cd /tmpcd /TMP 等效(Linux 严格区分)
  • Terminal 新窗口起始目录为 $HOME,可在"终端 → 设置 → 描述文件 → Shell → 启动时"中修改
  • macOS 没有 /bin/cd(Linux 也没有——cd 永远是内建)

来源

来源链接
POSIX cd 规范https://pubs.opengroup.org/onlinepubs/9699919799/utilities/cd.html
POSIX cd man pagehttps://www.man7.org/linux/man-pages/man1/cd.1p.html
bash 参考手册(cd 内建)https://www.gnu.org/software/bash/manual/html_node/Bourne-Shell-Builtins.html
GNU bash man pagehttps://man7.org/linux/man-pages/man1/bash.1.html
ShellCheck SC2164https://www.shellcheck.net/wiki/SC2164
The Bash Hackers Wiki: cdhttps://flokoe.github.io/bash-hackers-wiki/commands/builtin/cd/
整理时间2026-07-14