cd 命令详解与参数实测(Ubuntu 22.04)
cd 内建命令实测详解:绝对与相对路径、无参数与 ~ 回主目录、cd - 与 OLDPWD、-L/-P 软链切换、CDPATH 搜索路径、错误回显与退出码,全部命令与回显在 Ubuntu 22.04(bash 5.1.16)实测通过。
cd 命令详解与参数实测(Ubuntu 22.04)
目标:全面掌握 bash 内建 cd 的每一种用法——绝对/相对路径切换、无参数与
~回主目录、cd -与 OLDPWD、-L/-P软链切换、CDPATH 目录搜索路径、各类错误回显与退出码,全部命令与回显已在 Ubuntu 22.04(GNU bash 5.1.16)实测通过。
一、环境信息
本节记录实测环境与演示数据。cd 与其他命令有个本质区别:它是 shell 内建命令,没有 /bin/cd 这样的独立程序——因为切换目录改变的是当前 shell 进程的工作目录,外部子进程无法替父进程完成这件事。版本信息按 bash 记录。
- 操作系统:Ubuntu 22.04.5 LTS(x86_64)
- Shell 与 cd 版本:GNU bash 5.1.16(cd 为 bash 内建命令,
type cd实测) - 演示目录:
/root/demo/cd/ - 测试数据:多级项目目录树 projects/web/src、projects/api、docs(含 readme.md),以及 realdir/ 与指向它的软链 linkdir(用于
-L/-P实测) - 登录用户:root(HOME=/root,回主目录实测依赖该值)
二、命令概览
2.1 语法与测试数据准备
cd 的语法是 cd [-L | [-P [-e]] [-@]] [dir]:dir 缺省时回到 HOME;dir 为 - 时回到上一次所在目录;-L 跟随软链(默认)、-P 使用物理目录结构。dir 不以 / 开头时,先在 CDPATH 各目录下搜索,找不到再按当前目录的相对路径处理。
💡 技术拓展:"在目录间快速跳转"有几种不同机制,横向对比——
方式 原理/职责 适用场景 cd dir 按绝对/相对路径直接跳转 日常切换,最基础 cd - 在"当前"与"上一次"两个目录间往返 两个目录间来回对照 pushd / popd / dirs 目录栈:压栈、弹出、多目录往返 多于两个目录轮换作业 CDPATH 预设搜索基址,cd 相对名先到基址找 常去固定根目录(如 /var/log) 本文主角是 cd 内置命令本身:
-、-L/-P与 CDPATH 都是它的既有能力,不需要额外工具;目录栈用法在 4.3 实战展开。
准备一套多级目录树作为切换靶场:
mkdir -p /root/demo/cd/projects/web/src /root/demo/cd/projects/api /root/demo/cd/docs
echo readme > /root/demo/cd/docs/readme.md
验证数据就绪:
$ ls -R /root/demo/cd
/root/demo/cd:
docs
projects
/root/demo/cd/docs:
readme.md
/root/demo/cd/projects:
api
web
/root/demo/cd/projects/api:
/root/demo/cd/projects/web:
src
/root/demo/cd/projects/web/src:
目录树三层结构清晰可见,后续所有切换实测都在它上面进行。
2.2 help cd 全量回显(内建命令帮助)
cd 是内建命令,帮助要用 help cd 查看(而非 man cd),完整输出如下:
cd: cd [-L|[-P [-e]] [-@]] [dir]
Change the shell working directory.
Change the current directory to DIR. The default DIR is the value of the
HOME shell variable.
The variable CDPATH defines the search path for the directory containing
DIR. Alternative directory names in CDPATH are separated by a colon (:).
A null directory name is the same as the current directory. If DIR begins
with a slash (/), then CDPATH is not used.
If the directory is not found, and the shell option `cdable_vars' is set,
the word is assumed to be a variable name. If that variable has a value,
its value is used for DIR.
Options:
-L force symbolic links to be followed: resolve symbolic
links in DIR after processing instances of `..'
-P use the physical directory structure without following
symbolic links: resolve symbolic links in DIR before
processing instances of `..'
-e if the -P option is supplied, and the current working
directory cannot be determined successfully, exit with
a non-zero status
-@ on systems that support it, present a file with extended
attributes as a directory containing the file attributes
The default is to follow symbolic links, as if `-L' were specified.
`..' is processed by removing the immediately previous pathname component
back to a slash or the beginning of DIR.
Exit Status:
Returns 0 if the directory is changed, and if $PWD is set successfully when
-P is used; non-zero otherwise.
帮助里可实测验证的两个要点:默认 DIR 是 HOME 变量的值(3.3 实测);CDPATH 定义搜索路径、以 / 开头的 dir 不使用 CDPATH(3.6 实测)。另外 cd --help 与 help cd 的输出完全一致,用 diff 验证:
$ diff <(help cd) <(cd --help); echo diff_exit=$?
diff_exit=0
💡 说明:cd 只有
-L、-P、-e、-@四个参数,没有 GNU 风格的长选项;--help在这里被当作"帮助"特殊处理,而不是一个真正的选项。
2.3 版本确认
确认 cd 的内建身份与 bash 版本:
$ type cd
cd is a shell builtin
$ type -a cd
cd is a shell builtin
$ bash --version | head -1
GNU bash, version 5.1.16(1)-release (x86_64-pc-linux-gnu)
type -a cd 只有一条内建结果——磁盘上不存在 cd 程序文件,这与 5 节 Q7 的原理说明呼应。
三、参数逐项实测
3.1 基本切换:绝对路径与相对路径
绝对路径以 / 开头、从根出发;相对路径从当前目录出发。交替实测(每步 cd 后用 pwd 确认位置):
$ pwd
/
$ cd /root/demo/cd
$ pwd
/root/demo/cd
$ cd projects
$ pwd
/root/demo/cd/projects
$ cd web/src
$ pwd
/root/demo/cd/projects/web/src
cd /root/demo/cd 是绝对路径一步到位;cd projects、cd web/src 是相对路径逐层深入,且一次可以跨多级(web/src)。cd 成功后 shell 同步更新 PWD 与 OLDPWD 两个变量(后者见 3.4)。
💡 提示:
cd web/src这类多级相对路径等价于cd web && cd src两次切换,但中途任一层不存在则整体失败、位置不变。
3.2 .. 与多级相对路径
.. 代表上一级目录,可以连用。从最深处的 web/src 出发:
$ cd /root/demo/cd/projects/web/src
$ pwd
/root/demo/cd/projects/web/src
$ cd ../..
$ pwd
/root/demo/cd/projects
$ cd ../docs
$ pwd
/root/demo/cd/docs
cd ../.. 一次上跳两级到 projects;cd ../docs 从 projects 上跳一级再进 docs。注意 .. 是按路径字符串逐段处理的(帮助原文 "..' is processed by removing the immediately previous pathname component"),配合软链时与 -P` 的行为差异见 3.5。
一个容易误判的边界:.. 的目标在当前目录的字面父目录下必须真实存在。若从 projects/web 出发执行 cd ../docs,因为 web 的父目录是 projects 而非 demo/cd,会失败(交互式实测,一条命令内完成"进目录、失败、确认位置"三步):
$ bash -ic 'cd /root/demo/cd/projects/web; cd ../docs; pwd' 2>&1 | grep -v 'job control' | grep -v 'process group'
bash: cd: ../docs: No such file or directory
/root/demo/cd/projects/web
报错后 pwd 打印的仍是出发地 projects/web——cd 失败不会"半移动"。在同一位置再验证目录内容未受影响:
$ cd /root/demo/cd/projects/web
$ pwd
/root/demo/cd/projects/web
$ ls
src
💡 提示:cd 是"全有或全无"的操作,失败时保持原位,脚本里可以放心地在 cd 之后继续写依赖新位置的逻辑(配合退出码检测更稳,见 3.7)。
3.3 无参数与 ~:回到主目录
不带参数的 cd 直接回到 HOME;~ 是 HOME 的展开形式,还能展开成 ~用户名。逐项实测:
$ echo HOME=$HOME
HOME=/root
$ cd
$ pwd
/root
$ cd ~
$ pwd
/root
$ cd ~/demo/cd/projects
$ pwd
/root/demo/cd/projects
$ cd ~root
$ pwd
/root
四种形态殊途同归:cd 与 cd ~ 都回到 /root(对应帮助原文 "The default DIR is the value of the HOME shell variable");cd ~/demo/cd/projects 演示 ~ 拼接子路径一步进项目目录;cd ~root 显式指定用户主目录,root 用户下与 ~ 等价。
💡 提示:
~展开(波浪号展开)是 shell 的功能,cd 之前就已完成。切换到其他用户的主目录用cd ~用户名,要求该用户主目录存在且有执行权限。
3.4 cd - 与 OLDPWD:在两个目录间往返
每次 cd 成功,shell 都把"出发地"记进 OLDPWD;cd - 就是切回 OLDPWD。实测往返:
$ cd /root/demo/cd/projects/web
$ pwd
/root/demo/cd/projects/web
$ cd /root/demo/cd/docs
$ pwd
/root/demo/cd/docs
$ echo OLDPWD=$OLDPWD
OLDPWD=/root/demo/cd/projects/web
$ cd -
/root/demo/cd/projects/web
$ pwd
/root/demo/cd/projects/web
$ cd -
/root/demo/cd/docs
$ pwd
/root/demo/cd/docs
$ echo ~-
/root/demo/cd/projects/web
细节有三:cd - 切换时会打印新目录(这正是它可作为"回声"用的原因);连续两次 cd - 在两个目录间来回;~- 是波浪号展开版的 OLDPWD,可以拼路径用(如 ~-/log)且不触发切换。
💡 提示:
cd -只有一层记忆(OLDPWD 被覆盖更新),不是无限栈;更长的历史回跳交给pushd/popd/dirs。
3.5 -L 与 -P:软链目录的两种切换
与 pwd 的 -L/-P 呼应:-L(默认)让 PWD 保留软链名,-P 切换后立即解析成物理路径。用软链 linkdir 实测:
$ mkdir -p /root/demo/cd/realdir
$ ln -s /root/demo/cd/realdir /root/demo/cd/linkdir
$ cd /root/demo/cd/linkdir
$ pwd
/root/demo/cd/linkdir
$ cd -P /root/demo/cd/linkdir
$ pwd
/root/demo/cd/realdir
$ cd -L /root/demo/cd/linkdir
$ pwd
/root/demo/cd/linkdir
默认(-L)进入后 pwd 显示软链名 linkdir;显式 -P 进入后 pwd 显示真实目录 realdir——-P 让 shell 直接把物理路径写入 PWD,省去事后 pwd -P 的换算(pwd 侧的对照实测见《pwd 命令详解与参数实测(Ubuntu 22.04)》3.4)。
💡 提示:写"必须在真实目录下运行"的脚本(如依赖
$(pwd)拼路径的备份脚本)时,开头用cd -P一劳永逸。
3.6 CDPATH:相对路径的搜索前缀
CDPATH 类似 PATH,为相对路径提供一组搜索起点(冒号分隔;以 / 开头的 dir 不使用 CDPATH)。先实测未设置时的失败,再设置后对比:
$ bash -ic 'unset CDPATH; cd /root && cd projects' 2>&1 | grep -v 'job control' | grep -v 'process group'
bash: cd: projects: No such file or directory
$ bash -ic 'export CDPATH=/root/demo/cd; cd /root && cd projects' 2>&1 | grep -v 'job control' | grep -v 'process group'
/root/demo/cd/projects
未设 CDPATH 时,在 /root 下 cd projects 找不到目标报错;设置 CDPATH=/root/demo/cd 后同样的命令命中 /root/demo/cd/projects 并打印完整命中路径——这与 cd - 的回显行为一致,提示用户实际去哪了。再用 pwd 与 echo 确认:
$ pwd
/root/demo/cd/projects
$ echo CDPATH=$CDPATH
CDPATH=/root/demo/cd
💡 提示:CDPATH 有个反直觉的坑——当前目录下明明有同名子目录,CDPATH 命中的却是别处的目录(bash 按 CDPATH 顺序先搜)。因此很多人只把它用于交互式跳转,脚本里坚持绝对路径或显式相对路径。
3.7 错误场景与退出码
三类典型错误实测:目标不存在、目标不是目录、相对目标不存在:
$ bash -ic 'cd /no/such/dir' 2>&1 | grep -v 'job control' | grep -v 'process group'
bash: cd: /no/such/dir: No such file or directory
$ bash -ic 'cd /etc/passwd' 2>&1 | grep -v 'job control' | grep -v 'process group'
bash: cd: /etc/passwd: Not a directory
$ bash -ic 'cd nosuchdir' 2>&1 | grep -v 'job control' | grep -v 'process group'
bash: cd: nosuchdir: No such file or directory
/etc/passwd 是文件而非目录,报 Not a directory——这条错误信息常被忽略,但排障时它准确指出了"路径存在、类型不对"。失败时退出码为 1:
$ bash -ic 'cd nosuchdir; echo exit=$?' 2>&1 | grep -v 'job control' | grep -v 'process group'
bash: cd: nosuchdir: No such file or directory
exit=1
帮助原文 Exit Status 的表述与此一致:"Returns 0 if the directory is changed, and if $PWD is set successfully when -P is used; non-zero otherwise."
💡 提示:交互 shell 下报错前缀是
bash:;在脚本文件里执行时前缀会带脚本名与行号(如myscript.sh: line 3:),错误正文完全相同。
四、实战示例
4.1 主目录与项目目录间的高效往返
用 ~ 展开 + cd - 组合,两个高频位置一键互切:
$ cd ~/demo/cd/projects
$ pwd
/root/demo/cd/projects
$ cd -
/root/demo/cd/projects/web
$ pwd
/root/demo/cd/projects/web
cd - 回到的正是进入项目目录前的位置(3.4 的 OLDPWD 机制在日常工作流里的样子)。
4.2 CDPATH 固定"项目根",任何位置一跳直达
把项目根写进 CDPATH 后,无论身处何处,cd projects 都能命中(3.6 实测的日常化用法):
$ export CDPATH=/root/demo/cd
$ cd projects
/root/demo/cd/projects
$ pwd
/root/demo/cd/projects
命中时打印完整路径,避免"以为自己切到了别处"的困惑。
4.3 脚本里强制物理路径运行
备份、同步类脚本对路径真实性敏感,开头一句 cd -P 固化物理位置:
$ cd -P /root/demo/cd/linkdir
$ pwd
/root/demo/cd/realdir
后续脚本里 $(pwd)、相对路径都基于真实目录,不再受软链名干扰。
4.4 脚本里 cd 失败立即中止
cd 失败退出码为 1(3.7 实测),脚本中典型的防御写法是 cd 目标 || exit 1:切换失败就终止脚本,绝不在错误目录里继续执行删除/覆盖类操作。判断依据即帮助原文的 Exit Status 约定,无需自行解析报错文本。
五、注意事项与常见问题
Q1:为什么找不到 /bin/cd,man cd 也查不到帮助?
cd 是 shell 内建命令,不对应任何独立程序(type -a cd 只返回 shell builtin,见 2.3)。帮助用 help cd,完整手册在 man bash 中搜 cd。
Q2:cd 后提示符里显示的是软链名,想显示真实目录怎么办?
用 cd -P 切换(3.5 实测),PWD 直接变为物理路径;或事后 cd -P . 原地刷新。-L 是默认行为,恢复软链显示用 cd -L .。
Q3:cd - 依赖什么?为什么有时失灵?
依赖 OLDPWD 变量,它在每次 cd 成功后被覆盖,unset OLDPWD 或在未 cd 过的新 shell 里 cd - 会失败。它是单层记忆,多级回跳用 pushd/popd。
Q4:设置了 CDPATH 后,脚本里的相对路径会"跳错"目录吗?
可能。CDPATH 对不以 / 开头的 dir 全局生效(帮助原文),当前目录反而不是第一优先。脚本里要么用绝对路径,要么开头 unset CDPATH(3.6 有对照实测)。
Q5:cd 失败的退出码是多少?如何让脚本在 cd 失败时停下?
失败退出码 1(3.7 实测)。脚本写 cd 目标 || exit 1,或开头 set -e 让 bash 自动在任一命令失败时退出。
Q6:~ 和 ~root 有什么区别?
~ 展开为当前用户的 HOME(root 下是 /root,3.3 实测两者同值);~用户名 展开为指定用户的主目录。普通用户执行 cd ~root 通常因权限被拒。
Q7:cd 能否带上 -L/-P 之外的长选项如 --help?
不能。内建命令没有 GNU 长选项体系,--help 被 bash 特殊处理为打印帮助(与 help cd 输出一致,diff 验证见 2.2),--bogus 这类则报 invalid option。
六、参考链接
- Bash 参考手册(Bourne Shell Builtins · cd):https://www.gnu.org/software/bash/manual/html_node/Bourne-Shell-Builtins.html
- Bash 参考手册(目录栈 pushd/popd/dirs):https://www.gnu.org/software/bash/manual/html_node/Directory-Stack-Builtins.html
- 本机帮助:
help cd、cd --help、man bash(Ubuntu 22.04 内置)