dirname 命令详解与参数实测(Ubuntu 22.04)
dirname 命令逐参数实测详解:默认取父目录、无斜杠路径返回 . 的规则、尾随斜杠处理、多参数逐行输出、-z NUL 结尾输出与 basename 组合拆路径,全部命令与回显在 Ubuntu 22.04(GNU coreutils 8.32)实测通过。
dirname 命令详解与参数实测(Ubuntu 22.04)
目标:全面掌握 GNU dirname 的每一个参数——默认取父目录、尾随斜杠处理、无斜杠路径返回
.的规则、多参数逐行输出、-zNUL 结尾输出,以及与 basename 组合成"路径拆分对",全部命令与回显已在 Ubuntu 22.04(GNU coreutils 8.32)实测通过。
一、环境信息
- 操作系统:Ubuntu 22.04.5 LTS(x86_64)
- dirname 版本:GNU coreutils 8.32(
dirname --version实测) - dirname 属于 coreutils 基础工具集,Ubuntu 22.04 默认预装,无需额外安装
- 演示目录:
/root/demo/dirname/ - 测试数据:
a/b/file.txt(两级嵌套目录下的文本文件)、c/d.md、软链ln.txt
二、命令概览
2.1 语法与测试数据准备
dirname 的基本语法为 dirname [OPTION] NAME...:输出每个 NAME 去掉最后一个非斜杠成分与尾随斜杠后的结果;若 NAME 中不含任何 /,输出 .(表示当前目录)。它与 basename 是一对——dirname 取路径的"目录部分",basename 取"文件名部分"。
💡 技术拓展:路径"拆解"是一族工具,除了 dirname 还有三种常见取法,横向对比——
工具 原理/职责 适用场景 dirname 去掉末段成分与尾随斜杠,输出目录部分 脚本里定位脚本所在目录、拼配置路径 basename 去前导目录成分取末段文件名 与 dirname 配对拆"目录 + 文件名" realpath 规范化为绝对路径,解软链、消 ..需要真实物理路径时 readlink -f 软链为主要目标的等价规范化 已知路径是软链时取最终指向 本文主角是 dirname:纯字符串处理、不访问文件系统,输出"目录部分"后常与 basename 组合完成整条路径的拆分。
先准备一套演示数据,后续所有参数实测都基于这套数据:
mkdir -p /root/demo/dirname/a/b /root/demo/dirname/c
printf 'demo\n' > /root/demo/dirname/a/b/file.txt
printf 'demo2\n' > /root/demo/dirname/c/d.md
ln -sf /root/demo/dirname/a/b/file.txt /root/demo/dirname/ln.txt
验证数据就绪:
$ ls -l /root/demo/dirname /root/demo/dirname/a/b
/root/demo/dirname:
total 8
drwxr-xr-x 3 root root 4096 Oct 15 06:08 a
drwxr-xr-x 2 root root 4096 Oct 15 06:08 c
lrwxrwxrwx 1 root root 31 Oct 15 06:08 ln.txt -> /root/demo/dirname/a/b/file.txt
/root/demo/dirname/a/b:
total 4
-rw-r--r-- 1 root root 5 Oct 15 06:08 file.txt
2.2 --help 全量回显
dirname --help 输出全部参数清单(实测原样保留,可作为参数速查):
Usage: dirname [OPTION] NAME...
Output each NAME with its last non-slash component and trailing slashes
removed; if NAME contains no /'s, output '.' (meaning the current directory).
-z, --zero end each output line with NUL, not newline
--help display this help and exit
--version output version information and exit
Examples:
dirname /usr/bin/ -> "/usr"
dirname dir1/str dir2/str -> "dir1" followed by "dir2"
dirname stdio.h -> "."
GNU coreutils online help: <https://www.gnu.org/software/coreutils/>
Report any translation bugs to <https://translationproject.org/team/>
Full documentation <https://www.gnu.org/software/coreutils/dirname>
or available locally via: info '(coreutils) dirname invocation'
💡 说明:dirname 只有一个功能参数
-z(NUL 结尾)。帮助第一二行概括了核心规则:去掉最后一个非斜杠成分与尾随斜杠;不含/时输出.。
2.3 版本确认
$ dirname --version | head -1
dirname (GNU coreutils) 8.32
三、参数逐项实测
3.1 默认用法:取父目录
不带参数,dirname 输出路径去掉末段后的目录部分:
$ dirname /root/demo/dirname/a/b/file.txt
/root/demo/dirname/a/b
$ dirname /root/demo/dirname/a/b
/root/demo/dirname/a
对文件路径输出其所在目录;对目录路径输出其上一级目录——dirname 同样不区分文件与目录、不做文件系统访问,只做字符串拆分,路径不必真实存在。对软链输出的也是链接路径的目录部分而非目标的目录:
$ dirname /root/demo/dirname/ln.txt
/root/demo/dirname
3.2 无斜杠路径:输出 .
路径中不含任何 / 时,按约定输出 .(当前目录):
$ dirname file.txt
.
file.txt 意味着"文件在当前目录下",所以其目录部分就是 .。这与 basename 对 .、.. 的原样输出形成互补,两命令合用总能把路径拆干净。
3.3 尾随斜杠行为
dirname 会先去掉路径末尾所有斜杠,再去掉最后一个非斜杠成分:
$ dirname /root/demo/dirname/a/b/
/root/demo/dirname/a
$ dirname /root/demo/dirname/a/b///
/root/demo/dirname/a
/root/demo/dirname/a/b/ 与 /root/demo/dirname/a/b/// 都输出 /root/demo/dirname/a:尾斜杠先被吞掉,b 作为最后一个非斜杠成分被去掉。帮助里的例子 dirname /usr/bin/ -> "/usr" 正是这一规则。
3.4 特殊路径:/ . .. 与单段相对路径
$ dirname /
/
$ dirname .
.
$ dirname ..
.
$ dirname a
.
四个特殊输入逐一说明:根目录 / 没有可去掉的成分,输出 /(与 basename 的根目录规则一致);. 无斜杠输出 .;.. 虽是两个字符但不含斜杠,输出 .;单段相对路径 a 同样输出 .。可见只要路径里没有 /,dirname 一律返回 .,与路径是否真实存在无关。
3.5 多参数逐行输出
dirname 默认就支持多个 NAME,逐行输出各自的目录部分(无需类似 basename 的 -a 开关):
$ dirname /root/demo/dirname/a/b/file.txt /root/demo/dirname/c/d.md
/root/demo/dirname/a/b
/root/demo/dirname/c
几个边界输入一起看:
$ dirname /etc/hosts /etc /file-at-root /root/demo/dirname/ln.txt
/etc
/
/
/root/demo/dirname
第一行 /etc/hosts 取到 /etc;第二行 /etc 本身是根下的一级目录,取到 /;第三行根目录下的文件 /file-at-root 也取到 /(路径不必存在,规则一致);第四行软链同样只拆字符串。
💡 提示:dirname 的多参数输出是逐行的,第 N 行输入对应第 N 行输出;配合
paste可把同一批路径的目录与文件名并排合成表格。
3.6 -z:NUL 结尾输出
-z(--zero)把每条结果的结尾从换行符换成 NUL 字节,是处理"文件名可能包含换行符"场景的安全分隔方式:
$ dirname -z /root/demo/dirname/a/b/file.txt | xxd
00000000: 2f72 6f6f 742f 6465 6d6f 2f64 6972 6e61 /root/demo/dirna
00000010: 6d65 2f61 2f62 00 me/a/b.
$ dirname /root/demo/dirname/a/b/file.txt | xxd
00000000: 2f72 6f6f 742f 6465 6d6f 2f64 6972 6e61 /root/demo/dirna
00000010: 6d65 2f61 2f62 0a me/a/b.
xxd 十六进制对照可见:默认输出以 0a(换行)结尾,-z 输出以 00(NUL)结尾。多结果场景下 NUL 逐个分隔:
$ dirname -z /root/demo/dirname/a/b/file.txt /root/demo/dirname/c/d.md | xxd
00000000: 2f72 6f6f 742f 6465 6d6f 2f64 6972 6e61 /root/demo/dirna
00000010: 6d65 2f61 2f62 002f 726f 6f74 2f64 656d me/a/b./root/dem
00000020: 6f2f 6469 726e 616d 652f 6300 o/dirname/c.
两个结果 /root/demo/dirname/a/b\0 与 /root/demo/dirname/c\0 以 NUL 相接。消费端用 xargs -0、read -d '' 读取。
3.7 --:显式结束选项列表
路径以 - 开头时,用 -- 隔开,避免被误认为参数:
$ dirname -- /root/demo/dirname/a/b/file.txt
/root/demo/dirname/a/b
POSIX 工具通用约定:-- 之后的参数一律按操作数处理。
四、实战示例
4.1 从完整路径中提取目录部分
$ f=/var/log/nginx/access.log; dirname $f
/var/log/nginx
4.2 与 basename 组合成"路径拆分对"
dirname 取目录、basename 取文件名,两者拼回原路径,是 shell 拆分路径的标准组合:
$ f=/var/log/nginx/access.log; echo $(dirname $f)/$(basename $f)
/var/log/nginx/access.log
$ f=/var/www/html/index.html; echo $(dirname $f)/$(basename -s .html $f).conf
/var/www/html/index.conf
第一条把路径完整拼回;第二条演示"目录不变、换文件主名与扩展名"的改写套路。
4.3 mkdir -p $(dirname ...):先建目标目录再写入
脚本里要把文件写到"路径还不存在的位置"时,用 $(dirname 目标路径) 先创建其父目录:
$ mkdir -p $(dirname /root/demo/dirnew/sub/target.txt) && cp /root/demo/dirname/a/b/file.txt $(dirname /root/demo/dirnew/sub/target.txt)/ && ls -R /root/demo/dirnew
/root/demo/dirnew:
sub
/root/demo/dirnew/sub:
file.txt
$(dirname ...) 计算出 /root/demo/dirnew/sub,mkdir -p 连同缺失的上级一并创建,cp 写入后 ls -R 确认结构正确。这一写法在备份脚本、日志落地脚本中极为常用。
五、注意事项与常见问题
Q1:dirname file.txt 为什么输出 .?
路径中不含 / 时按 POSIX 约定输出 .(当前目录),表示"该文件的目录就是这里"(见 3.2)。
Q2:dirname /etc 输出 / 还是空?
输出 /。一级绝对路径去掉末段后就是根目录;dirname / 同样输出 /(见 3.4、3.5)。
Q3:dirname .. 为什么输出 . 而不是 /?
.. 不含斜杠字符,命中"无斜杠输出 ."规则;dirname 不解析路径语义,.. 在它眼里只是普通字符串(见 3.4)。
Q4:尾随斜杠会影响结果吗?
会先被吞掉再拆分:dirname /a/b/ 等价 dirname /a/b,多个尾斜杠也一样(见 3.3)。
Q5:能解析软链的真实目录吗?
不能。dirname 是纯字符串拆分,对软链只输出链接路径的目录部分;要解析真实目标用 readlink -f 再拆(见 3.1 实测)。
Q6:多个路径为什么要用 -z?
默认换行分隔在文件名含换行符时会产生歧义;-z 用 NUL 分隔,配合 xargs -0 全程安全(见 3.6)。
Q7:脚本里该用 dirname 还是 ${var%/*}?
两者多数场景等价,但 ${var%/*} 对无斜杠路径会返回原串而非 .,且对 file.txt 这类输入行为不一致。dirname 语义自明、边界规则符合 POSIX,跨 shell 更稳(见 Q1)。
Q8:mkdir -p $(dirname X) 会不会误建目录?
只会创建 X 的父目录链,X 本身不会被建成目录;这正是该组合的正确用法(见 4.3 实测)。
六、参考链接
- GNU coreutils 官方手册(dirname 章节):https://www.gnu.org/software/coreutils/manual/html_node/dirname-invocation.html
- 在线文档:https://www.gnu.org/software/coreutils/dirname
- 本机帮助:
dirname --help、man dirname(Ubuntu 22.04 内置)