
在Linux下干活cd大概是我敲得最多的一个命令。你可能会说这玩意儿太基础但真往细里抠能把目录切换玩明白的人其实不多。这篇文章不打算做教科书式的概念复述而是从我日常排查、写脚本、远程维护的真实场景出发把cd的路径规则、常用参数、效率技巧和脚本里的特殊坑一次性讲透。无论你是刚接触Linux的运维新人还是写过几年shell脚本的老手应该都能从里面翻出点自己平时没注意到的细节。一句话先给结论cd不只是“进目录”这么简单它背后涉及路径解析、符号链接、环境变量、子shell行为甚至还是Shell内建命令与外部程序的区别。把这些理解了你的Linux操作会顺滑很多。1. 基础用法先说清路径规则再搞懂三个高频参数cd的全称是change directory作用就是切换当前工作目录。执行这条命令后Shell会把当前工作目录Current Working Directory改到你指定的路径而后续所有基于相对路径执行的命令都会拿这个新目录当作起点。所以聊cd的第一步不是急着背参数而是先搞明白Shell眼中的路径到底长什么样。1.1 PWD先行绝对路径与相对路径的底层逻辑很多新手学cd时容易蒙是因为碰到了一个日常概念什么是绝对路径什么是相对路径。绝对路径一定以/开头从根目录写起比如/etc/nginx/conf.d。无论你现在在哪个目录只要这个绝对路径真实存在cd都能直接过去。相对路径不以/开头它是从“当前位置”出发的一条描述。你可以把它理解成问路时对方说“出门右转再左转”而绝对路径说的是“北京市海淀区某大厦某室”。举个例子当前目录是/home/user你执行$ pwd /home/user $ cd /etc/nginx # 绝对路径一下到位 $ cd ../.. # 相对路径往上退两级回到 / $ pwd /很多人写命令时容易犯一个毛病不看当前位置直接信心满满地写一个相对路径结果立刻收到“No such file or directory”。我自己的习惯是在敲任何cd之前先敲一下pwd确认自己站在哪。看起来耽误两秒钟但能省掉后面一堆排查时间。另外Shell在解析相对路径时会结合当前目录。比如你执行cd /etc/nginx/conf.d如果敲错一个字母Shell不会自动帮你纠正所以Tab补全真的要用起来。连续多级路径让Tab帮你扫一遍既省事又不会手滑。1.2 裸cd回家、cd -回上个目录最容易被忽略的便捷用法不带任何参数执行cd效果等于回到当前用户的HOME目录。也就是说你不用总记着/home/user这样的长路径$ cd $ pwd /home/user更实用的是cd -。它会在Shell里记住“上一次所在的目录”然后直接切回去。这个功能靠环境变量OLDPWD实现执行cd -时Shell实际上是在执行cd $OLDPWD并且把切换后的目录打印出来。$ cd /var/log $ cd /etc $ cd - /var/log $ pwd /var/log这条命令在运维场景里尤其好用。比如刚才还在/var/log/nginx看错误日志临时去/etc/nginx改了个配置想切回日志目录一个cd -就完事不用重复敲长路径。我见过不少同事用cd -时碰到一个意外刚开始某个目录已经切换过一次cd -只会在两个目录之间来回跳不会记得更早的历史。想要记住三个甚至更多目录就得用后面要讲的目录栈pushd/popd。1.3 -L和-P符号链接里的“逻辑路径”与“物理路径”这一小节是很多人没深究过的cd参数-L和-P。默认情况下Bash的cd采用-L逻辑模式也就是碰到符号链接时$PWD环境变量会保留你使用的“逻辑路径”保持符号链接的名字。看演示$ ln -s /var/www /web $ cd /web $ pwd /web $ cd -P /web $ pwd /var/www创建了一个指向/var/www的软链接/web。直接cd /web时pwd显示的是/web这个逻辑路径而加上-P参数后Shell会解析符号链接一直找到真实物理路径/var/www并把$PWD也改成物理路径。这个区别在写脚本或做路径判断时特别重要。比如你基于$PWD去拼接文件路径软链接路径可能和程序实际工作目录不一致导致配置写错位置。排查问题的时候用pwd -P可以快速看到当前目录的物理路径。但也别盲目全用-P。有些场景比如需要保持“用户看到的路径恒定”-L反而是期望行为。说白了脚本里尽量明确告诉Shell你要用哪种模式别赌默认值。2. 进阶技巧让目录切换更快更顺手基础操作聊完该谈效率了。日常写命令时单纯靠cd加完整路径来回跳确实不够快尤其是要在多个项目目录、日志目录、配置文件目录之间反复横跳的时候。下面这几个技巧能明显减少你的按键次数。2.1 目录栈三兄弟pushd、popd、dirspushd和popd不是cd但它们解决的问题恰恰是cd -照顾不到的跨多个目录来回切换。pushd可以把当前目录压入一个“目录栈”然后切换到新目录popd则从栈顶弹出目录并切回去dirs -v能显示当前栈中的所有目录并带序号。$ cd /tmp $ pushd /etc /tmp /etc $ pushd /var/log /tmp /etc /var/log $ dirs -v 0 /var/log 1 /etc 2 /tmp此时想回/etc不用敲完整路径直接popd就回到栈顶的下一个目录再popd一次就回/tmp。如果栈里目录很多还可以用pushd 1交换栈项位置达到跳转效果。我实际工作中用得最多的场景是“临时去看另一个目录看完还要回来”。比如我正在/home/user/projectA写代码突然要检查/var/log/nginx/error.log可以$ pushd /var/log/nginx $ tail -n 50 error.log $ popd这样既去了目标目录又能一键弹回原来的项目目录比cd -更可靠因为栈没有数量限制。2.2 CDPATH和别名把长路径翻译成短命令CDPATH是一个容易被忽略的环境变量作用和PATH类似。当cd的目录不是绝对路径、也不以./或../开头时Shell会按CDPATH里定义的目录列表去搜索。举个例$ export CDPATH~/projects $ cd myblog /home/user/projects/myblog这样只要~/projects下面有myblog目录你在任何地方执行cd myblog都能直接进去。听起来很方便但这里有个大坑CDPATH会改变cd的解析逻辑容易误进入同名目录在脚本里尤其危险。所以我一般只在交互式Shell中临时设置或者干脆不用而是用别名和函数来解决问题。别名是更直白的做法。你可以在.bashrc里写alias blogcd ~/projects/myblog alias logscd /var/log/nginx以后敲blog就相当于cd ~/projects/myblog。别觉得这算偷懒这才是命令行该有的效率。补充一个Bash 4.4之后才有的autocd选项开启后你输入一个目录名且该目录存在Shell会自动当成cd执行。不过它会和别名、函数产生一些隐晦的交互我很少在正式环境开启知道有这个东西就够了。2.3 动态目录切换用命令替换拿到目标路径很多时候我们要切到的目录是动态算出来的不是写死的。这时候cd就得配合命令替换$(...)或者变量使用。最经典的是脚本里获取“脚本自身所在目录”#!/usr/bin/env bash SCRIPT_DIR$(cd $(dirname ${BASH_SOURCE[0]}) pwd) cd $SCRIPT_DIR echo 当前工作目录: $PWD这段代码的原理是先用dirname取出脚本路径的目录部分然后cd进去再执行pwd把相对路径转换成绝对路径。为什么多此一举因为dirname返回的可能是相对路径不转成绝对路径的话脚本一旦中途切换目录后续引用就全错了。再比如从一个配置文件里读目录$ cd $(cat /data/app_workdir.txt)注意命令替换结果一定要用双引号包起来。如果路径里有空格不带引号的cd会把它拆成多个参数立刻报错。2.4 交互式增强武器fzf和zoxide如果你的Shell是Bash或Zsh并且愿意装一点工具目录切换体验会上一个新台阶。fzf提供的AltC快捷键可以让你用模糊搜索的方式选择目录并进入配合CtrlT搜索文件名效率非常高。它是交互式Shell场景下的杀手级工具。zoxide则是更聪明的“跳转工具”。它根据你访问目录的频率和最近使用时间维护一个数据库只要你敲z project它自动猜测最可能的目录并跳过去。用了之后你会发现自己再也回不去手敲长路径的日子了。不过这一节属于锦上添花不是cd本身的必需能力。如果你只想把基础搞好前面的内容已经完全够用。3. 脚本实战cd的安全写法与父子Shell边界交互式Shell里用cd错了就错了报错再改但写进脚本的cd一旦出错损害会被放大。脚本中每一行cd都必须想清楚它会影响谁失败后怎么办3.1 直接执行与source执行cd到底会不会改当前Shell新手最容易踩的一个坑是搞不清脚本里的cd会不会改变当前Shell的目录。如果你执行的是./test.sh这个脚本会运行在一个子Shell中脚本里的cd只影响子Shell的工作目录脚本执行结束后当前Shell的目录完全不变。$ cat test.sh #!/usr/bin/env bash cd /tmp $ ./test.sh $ pwd # 还是原来的目录但如果你用source test.sh或者.方式执行脚本就在当前Shell进程里跑cd会直接改变当前Shell的目录。$ source test.sh $ pwd # 变成了 /tmp这个区别决定了你怎么设计脚本。如果希望脚本改变当前Shell的工作目录必须用source反之如果不希望脚本影响当前Shell可以直接以子Shell方式执行。对于那些“临时进去干完活要返回”的场景还可以用圆括号包起来$ ( cd /opt/backend ./stop.sh ) $ pwd # 依旧在原来的目录圆括号里的内容会独立在子Shell中运行cd出不来这是脚本里防“环境传染”的一个很实用手段。3.2 六个安全写法值得写进你的脚本规范我在实际项目中整理过一组cd的规范每一条都是从线上问题里逼出来的第一先判断目录存在再执行cd。不要假设路径一定存在target/var/lib/myapp if [[ -d $target ]]; then cd $target else echo 目录不存在: $target 2 exit 1 fi第二变量必须加双引号。路径含空格是Linux里的常态cd $dir在$dir为空时还可能变成裸cd直接把你带回HOME这是灾难。所以永远写cd $dir。第三用cd ... ...串联后续动作。只有切换成功才继续执行避免你在错误目录里执行了后续命令。cd $project_dir make build第四在set -e脚本里cd失败会让脚本直接退出。这本身不是坏事但你要知道这个行为并且提前给用户友好的报错信息不能只留一个原始错误。第五避免在脚本里依赖CDPATH。脚本应该使用绝对路径或显式相对路径一旦依赖交互式环境变量换台机器就跑结果不一样。第六注意cd的退出码。很多脚本新手只关心前一条命令的退出码却忽略cd失败后的返回。如果cd失败但你继续执行后续命令会在错误环境下运行问题会变得非常难查。3.3 用函数封装“进入目录并执行动作”除了裸写cd把逻辑封装成函数是更高级的做法。比如经典的“创建目录并进入”mcd() { mkdir -p $1 cd $1 }我常年在.bashrc里放这样一个函数mcd newdir会先创建目录必要时带上所有父目录再切进去。比mkdir cd两条命令舒服多了。再比如处理固定工作区goto() { case $1 in web) cd /var/www/html ;; logs) cd /var/log/nginx ;; proj) cd ~/work/project ;; *) if [[ -d $1 ]]; then cd $1; else echo 未知目标: $1; fi ;; esac }函数的好处是你可以写条件判断、循环、错误处理这是别名做不到的。写了之后记得source ~/.bashrc让配置生效。4. 常见问题与排查技巧实录最后这部分我把这些年实际踩过、帮别人排查过的cd相关问题集中记录下来。每一个都对应真实场景不是凭空想出来的。4.1 ls能看到目录cd却进不去权限小把戏这种问题最容易让人发懵我用ls明明能看到这个目录为什么cd报“Permission denied”原因在于Linux对目录的权限规定里执行权限x才是“可以穿过/进入目录”的关键。如果你对一个目录只有读权限r没有执行权限x你可以列出目录里的文件名却不能进入它也无法访问内部文件的元数据。$ ls -ld /data/restricted dr--rwx--- 2 root root 4096 ... $ cd /data/restricted -bash: cd: /data/restricted: Permission denied遇到这种问题先用ls -ld看目录权限确认自己有没有进入权限。解决方案通常是用有权限的用户执行或者让管理员帮你加x权限chmod ux /data/restricted4.2 路径有空格、中文、通配符引号和Tab补全才是解药cd My Documents这样写是错的因为Shell默认用空白作为参数分隔路径一旦有空格就会被拆成两个参数。正确写法是使用引号包裹或者用反斜杠转义空格$ cd My Documents $ cd My\ Documents中文路径同理直接放进引号里就行。而Tab补全会自动给你转义空格和特殊字符所以真正的高效做法是敲到路径前缀后按Tab让Shell帮你把引号加好。通配符的情况更隐蔽。cd /tmp/dir*如果/tmp下正好只有一个匹配项Shell会先展开它再交给cd所以能用但如果匹配到多个目录cd会收到多个参数直接报错。所以通配符路径建议先展开到变量确认只有一个匹配项后再用。4.3 符号链接带来的“迷路”和“找不到”前面讲过-L和-P这里说两个常见事故。事故一目录在软链接下脚本里判断$PWD符合预期但程序实际访问到的却是物理路径导致配置文件路径对不上。解决办法就是显式用cd -P或者用pwd -P检查真实路径。事故二软链接的目标被删了cd /opt/broken_link会报No such file or directory。你可能会奇怪链接明明还在啊。这时候可以检查$ ls -l /opt/broken_link $ readlink -f /opt/broken_linkreadlink -f会尝试解析完整物理路径如果目标不存在它会放在最后显示一个不存在的路径。这是排查软链接失效的利器。4.4 alias把cd带“歪”了用builtin cd找回原厂功能有些发行版或用户自己会在配置里加alias cdcd -P甚至更极端的alias cdecho cd我见过有人调试时这么干然后忘了改回来。这种时候你敲cd执行的根本不是原版命令。先看一下当前cd到底是个啥$ type cd cd is aliased to cd -P如果被alias影响了想临时绕开它反斜杠是通用杀招$ \cd /tmp在Bash里还可以显式调用内建版本$ builtin cd /tmp排查“cd行为怪异”的问题第一步永远是type cd。同理如果你自定义了一个cd()函数它也会覆盖内建命令。4.5 高频报错速查表报错信息常见原因解决方案cd: No such file or directory目录不存在、变量未展开、软链接失效先ls确认路径检查变量是否为空用readlink -f查软链接cd: Permission denied目标目录或路径上级目录缺少执行权限用ls -ld查看权限补上x权限或切换用户cd: Not a directory路径最终指向的是一个普通文件而不是目录检查目标路径确认要用的是目录而不是文件cd -: command not found某些极简镜像里没有加载OLDPWD相关环境其实更可能是cd被覆盖用type cd检查临时使用builtin cd这里多说一句cd是Shell内建命令不是/bin/cd这样的外部程序。你在脚本里用which cd往往查不到东西因为压根没有这个外部命令。想看cd的帮助文档直接在Bash里执行help cd比网上搜到的又全又准。最后再分享一个小技巧。我自己写脚本时最常用的就是那段“获取脚本所在目录”的代码几乎成了肌肉记忆而在交互式终端里我更依赖pushdpopd的组合很少只用一个cd -。这些都是生产环境里一次次踩坑换来的习惯不是读文档读出来的。希望这篇关于cd的实用操作总结能让你下一次面对目录切换时心里更踏实。