Skip to content

pwd

pwdprint working directory)看似简单到只有两个选项,但恰恰是这层"简单"让很多人从未深究过它真正的知识纵深——shell 内建与外部二进制的默认行为完全相反,符号链接下的路径分歧更是脚本 bug 的温床。

速查卡片

需求命令
显示当前目录pwd
显示物理路径(解析符号链接)pwd -P
显示逻辑路径(保留符号链接)pwd -L
使用外部命令(非 shell 内建)/bin/pwdcommand pwd
查看 PWD 环境变量echo $PWD
查看上一个目录echo $OLDPWD
获取脚本所在目录DIR="$(cd "$(dirname "$0")" && pwd)"
验证在符号链接目录中pwd && pwd -P(两输出不同则是)

概述

pwd 是 Linux 最基本的命令之一,用途单一:输出当前工作目录的绝对路径。但它的实现有两种形态:

  • shell 内建命令:bash/zsh/dash 等 shell 内置的实现,直接读取 $PWD 环境变量,极快
  • 外部二进制 /bin/pwd:GNU coreutils 提供的独立程序,通过 getcwd() 系统调用从文件系统逐级追溯

两者在默认行为上存在关键差异:shell 内建默认 -L(逻辑路径,保留符号链接),GNU /bin/pwd 默认 -P(物理路径,解析符号链接)。这个差异是 pwd 文档的核心价值所在。

命令签名

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

# GNU coreutils 签名
pwd [OPTION]...

选项

选项长选项说明
-L--logical$PWD 环境变量取值输出,保留路径中的符号链接分量(shell 内建默认)
-P--physical解析全部符号链接,输出真实文件系统路径(/bin/pwd 默认)
--help显示帮助信息(仅外部命令)
--version输出版本信息(仅外部命令)

默认行为差异:POSIX 规范规定不指定选项时等效 -L;GNU coreutils man page 写明 "If no option is specified, -P is assumed"。这不是 bug——/bin/pwd 的 man page 同时备注:"Your shell may have its own version of pwd, which usually supersedes"。日常交互中你用的 pwd 大概率是 shell 内建,默认 -L

同时指定 -L-P

POSIX 规定:最后一个选项生效。pwd -L -P 等价于 pwd -P

shell 内建 vs 外部二进制

这是整篇文档最深的知识点。两套实现不是"哪个更好"的问题,而是行为不同,选错会出 bug。

维度shell 内建 pwd/bin/pwd(GNU coreutils)
默认行为-L(逻辑路径)-P(物理路径)
实现方式读取 $PWD 环境变量调用 getcwd() 系统调用
性能极快,零文件系统 I/O逐级遍历目录树,受磁盘 I/O 影响
权限依赖无需父目录读权限需要对路径上每一级目录的读权限
目录被删除时仍输出旧 $PWD输出带 (unreachable) 的相对路径或报错
长选项无(bash 只支持 -L/-P支持 --logical --physical

如何精确选择

bash
type pwd          # 查看当前 pwd 是哪种实现
pwd               # shell 内建,默认 -L
builtin pwd       # 强制使用内建
command pwd       # 强制使用外部命令(POSIX 方式)
\pwd              # 反斜杠绕过别名,同样走外部命令
/bin/pwd          # 直接调用二进制
env pwd           # 在干净环境下运行(不继承 shell 别名/函数)

基本用法

示例一:最简用法

bash
$ pwd
/home/zolin/projects

无选项时输出当前工作目录的绝对路径。由于 shell 内建默认 -L,如果通过符号链接进入目录,输出的是你走过去的那条路而不是真实路径。

示例二:典型场景——确认你在哪里

bash
$ cd /etc/nginx
$ pwd
/etc/nginx
$ ls
nginx.conf  sites-available  sites-enabled

进入目录后 pwd 确认位置,是日常操作的标准习惯。

示例三:进阶场景——符号链接下的路径分歧

bash
# 准备环境
$ mkdir -p /tmp/real/project
$ ln -s /tmp/real /tmp/link

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

$ pwd          # shell 内建,默认 -L
/tmp/link/project

$ pwd -P       # 强制物理路径
/tmp/real/project

$ /bin/pwd     # 外部命令,默认 -P
/tmp/real/project

$ echo $PWD    # 环境变量,永远是逻辑路径
/tmp/link/project

四种方式,三种结果。 如果你的脚本依赖路径判断(如检查是否在某个特定目录下),用错了就会静默出错。

示例四:冷门场景——目录被删除后

bash
$ mkdir /tmp/testdir && cd /tmp/testdir
$ rmdir /tmp/testdir          # 删除当前目录

$ pwd                          # shell 内建:仍输出旧值
/tmp/testdir

$ /bin/pwd                     # 外部命令(Linux ≥ 2.6.36)
(unreachable)/tmp/testdir      # 带前缀标记,且是相对路径

自 Linux 2.6.36 起,getcwd() 在目录被删除时返回带 (unreachable) 前缀的相对路径——但这违反了 POSIX 对绝对路径的要求。

$PWD 环境变量

$PWD 是 shell 维护的核心变量,记录当前的逻辑工作目录。

特性说明
初始化shell 启动时设置,随每次 cd 更新
内容绝对路径,可能包含符号链接分量
有效性POSIX:若 $PWD 不包含 ... 分量,pwd -L 直接用它
超过 PATH_MAXPOSIX:实现可选择回退到 -P
用户修改后果POSIX:应用自行设置或取消 $PWD 后,pwd 行为未定义
继承子进程继承当前 shell 的 $PWD

$PWD "变味"场景

如果某级父目录被重命名,$PWD 不会自动更新:

bash
$ cd /tmp/foo/bar
$ mv /tmp/foo /tmp/foo2        # 重命名父目录
$ pwd                           # shell 内建:仍输出旧路径
/tmp/foo/bar
$ /bin/pwd                      # 外部命令:失败,因为路径已不存在
/bin/pwd: couldn't find directory entry in '..' with matching i-node

$OLDPWD

bash
$ cd /tmp
$ cd /etc
$ echo $OLDPWD
/tmp
$ cd -                          # 等价于 cd "$OLDPWD"
/tmp

cd - 在最近两个目录间切换,适合频繁在两个工作区间跳转的场景。

符号链接深入

符号链接是理解 pwd 全部价值的场景。这里集中展开。

cd .. 的行为差异

在符号链接目录中,cd .. 的行为取决于 shell 的物理/逻辑模式:

bash
$ mkdir -p /tmp/a/b
$ ln -s /tmp/a /tmp/link

$ cd /tmp/link/b

# 逻辑模式(默认)
$ cd .. && pwd
/tmp/link         # 沿符号链接路径返回

# 物理模式
$ cd -P .. && pwd
/tmp/a            # 沿真实文件系统返回

这就是很多人的困惑来源——"明明在 /tmp/link/bcd .. 之后怎么回到了 /tmp/link 而不是 /tmp/a?"

全局切换默认行为

shell命令效果
bashset -Pset -o physicalcdpwd 默认使用物理路径
bashset +Pset +o physical恢复默认逻辑路径
zshset -wset -o chaselinks同上
永久生效写入 ~/.bashrc / ~/.zshrc

内核视角:/proc/self/cwd

Linux 内核只维护已解析的真实路径,不关心符号链接:

bash
$ cd /tmp/link/b
$ ls -l /proc/self/cwd
lrwxrwxrwx 1 root root 0 Jul 14 12:00 /proc/self/cwd -> /tmp/a/b

无论你是通过哪个符号链接过来的,内核只看你"真正"在哪个目录。

目录栈操作(pushd / popd / dirs)

bash 和 zsh 提供了基于栈的目录导航,与 pwd 配合使用:

bash
$ cd /tmp
$ pushd /etc     # 将 /etc 压栈并切换
/etc /tmp

$ pushd /var     # 将 /var 压栈并切换
/var /etc /tmp

$ dirs           # 查看栈内容
/var /etc /tmp

$ dirs -v        # 带编号查看
 0  /var
 1  /etc
 2  /tmp

$ popd           # 弹出栈顶,切换到下一个
/etc /tmp

$ pwd
/etc

$DIRSTACK 是 bash 暴露的目录栈数组变量,可在脚本中使用。

脚本中的 pwd

获取脚本所在目录

这是 pwd 在脚本中最重要的用途:

bash
#!/bin/bash
# 经典写法:获取脚本所在目录的绝对路径
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
echo "脚本位于:$SCRIPT_DIR"

逐步拆解:

  1. $0:脚本的调用路径(可能是相对路径,如 ./script.sh
  2. dirname "$0":提取目录部分
  3. cd 到该目录
  4. pwd:输出绝对路径

$0 的陷阱:如果脚本被 source 引入(. script.shsource script.sh),$0 可能是调用者的名称而不是脚本自身。此时应用 ${BASH_SOURCE[0]} 替代。

符号链接安全的版本

如果脚本本身是符号链接:

bash
# GNU 环境(Linux)
SCRIPT_DIR="$(cd "$(dirname "$(readlink -f "$0")")" && pwd)"

# 兼容 macOS 的写法(macOS 没有 readlink -f)
if command -v realpath &>/dev/null; then
    SCRIPT_DIR="$(cd "$(dirname "$(realpath "$0")")" && pwd)"
else
    SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"
fi

存储后跳回

bash
HERE="$PWD"
cd /some/other/place
# ... 工作 ...
cd "$HERE"        # 回到原目录

pushd/popd 更可移植(兼容所有 POSIX shell)。

相关命令对比

命令功能输出示例(在符号链接目录中)
pwd逻辑路径(shell 内建默认)/tmp/link/project
pwd -P物理路径/tmp/real/project
/bin/pwd物理路径(外部命令默认)/tmp/real/project
echo $PWDPWD 变量值/tmp/link/project
realpath .解析当前目录真实路径/tmp/real/project
readlink -f .GNU 版解析真实路径/tmp/real/project

退出码

含义
0成功
>0出错(无父目录读权限 + 使用 /bin/pwd 等)

POSIX 特别规定:出错时不得向 stdout 写入任何内容。这一要求的背景是历史上大量脚本不检查 pwd 的退出码,如果输出了不完整的路径可能造成灾难性后果。

注意事项

  • /bin/pwd 依赖父目录读权限:如果路径上任何一级目录不可读,外部命令会失败,shell 内建不受影响
  • $PWD 可能已被污染:如果脚本或程序修改了 $PWDpwd -L 会输出错误值——POSIX 将其标注为"未定义行为"
  • 长路径风险:POSIX 指出,路径超过 PATH_MAXpwd 输出的结果可能导致 cd 失败,需拆分为多段
  • 不要混淆 pwdecho $PWD:前者经过(至少内建层面)一定的校验,后者是裸读变量

常见误区

❌ 误区✅ 正解
"pwd 就是 /bin/pwd"日常交互中用的是 shell 内建,默认行为不同(-L vs -P
"cd .. 总是回到物理父目录"逻辑模式下沿符号链接路径返回,可能不是你预期的目录
"$PWD 一定是真实路径"通过符号链接进入时包含链接分量,用 pwd -P 求真值
"pwd 永远不会失败"/bin/pwd 在父目录无读权限时会报错退出
"$(dirname "$0") 就能拿到脚本目录"$0 可能是相对路径或符号链接,需 cd + pwd 转绝对
"所有 shell 的 pwd 行为一致"bash/zsh/dash 对 -L/-P 默认处理一致,但 /bin/pwd(GNU)默认 -P

版本信息

事件版本/日期
首次纳入 POSIXIssue 2
新增 -P / -L 选项POSIX Issue 6(IEEE P1003.2b)
getcwd() 行为变化同步POSIX Issue 7(Austin Group Interpretation #140)
getcwd() 增加 (unreachable) 前缀Linux 2.6.36
GNU coreutils 当前版本9.11(2026-04)

macOS 特别说明

  • macOS 默认 shell 为 zsh
  • zsh 内建 pwd 默认 -L,支持 -P-L,行为与 bash 一致
  • macOS 的 /bin/pwd 来自 BSD(非 GNU),选项支持 -L/-P,但不支持长选项 --logical/--physical
  • zsh 的 $PWD 变量标记为只读,不可直接赋值修改
  • macOS 没有 readlink -f(BSD 版本不支持 -f 选项),可用 realpath 替代

来源

来源链接
GNU coreutils man pagehttps://man7.org/linux/man-pages/man1/pwd.1.html
POSIX 规范https://pubs.opengroup.org/onlinepubs/9699919799/utilities/pwd.html
bash 参考手册(pwd 内建)https://ftp.gnu.org/old-gnu/Manuals/bash/html_chapter/bashref_4.html
getcwd(3) Linux man pagehttps://man7.org/linux/man-pages/man3/getcwd.3.html
GNU coreutils 官方文档https://www.gnu.org/software/coreutils/manual/html_node/pwd-invocation.html
Stack Overflow: shell builtin vs /bin/pwdhttps://stackoverflow.com/questions/3134479/
整理时间2026-07-14