%n 输出原始路径而非 basename,因 stat 不解析路径;需用 basename 或 ${f##*/} 提取纯文件名,再结合 stat 获取元数据。

stat -C "%n" 本身不能直接输出“纯文件名”(即不带路径的 basename),它输出的是 stat 命令所接收的原始路径参数字符串。比如你运行 stat -C "%n" /home/user/docs/report.txt,结果就是 /home/user/docs/report.txt,不是 report.txt。
为什么 %n 不等于 basename?
%n 是 stat 的格式占位符,含义是 “file name”,但这里的“name”指传入 stat 的完整路径字面量,不是经过解析后的文件名部分。stat 不做路径拆解,它只读取并按格式输出你给它的输入路径。
要真正提取纯文件名(去掉目录路径),必须配合 shell 的路径处理机制,不能只靠 stat。
推荐做法:用 basename + stat 组合获取纯名与元数据
若你的归档脚本需要「纯文件名」+ 其他 stat 信息(如修改时间、大小),最佳方式是先用 basename 提取名称,再用 stat 获取所需字段——或一步到位用 stat 输出多个字段,再用 basename 单独处理路径:
- ✅ 安全可靠:
basename "/path/to/file.log"→file.log - ✅ 同时取时间和纯名:
printf "%s\t%s\n" "$(basename "$f")" "$(stat -C "%y" "$f")" - ✅ 批量处理示例(循环归档):
for f in /source/*.log; do
[ -f "$f" ] || continue
name=$(basename "$f")
mtime=$(stat -C "%y" "$f" | cut -d' ' -f1) # 取日期部分
cp "$f" "/archive/${mtime}_${name}"
done
进阶技巧:用 stat -c 配合 shell 参数扩展(免调用 basename)
如果你坚持用 stat 单命令,并且路径变量已知,可利用 bash 的 ${f##*/} 直接截取末段:
-
f="/var/log/app/error_202405.log"; echo "${f##*/}"→error_202405.log - 结合 stat 使用:
stat -c "Name: ${f##*/}, Size: %s, Mod: %y" "$f"(注意:%s %y 等仍作用于 "$f",${f##*/} 是 shell 展开,非 stat 解析) - ⚠️ 注意:此法依赖变量 $f 已赋值,不能写成
stat -c "%n → ${%n##*/}"(stat 不支持嵌套 shell 展开)
避坑提醒:常见错误写法
- ❌
stat -C "%n" /data/file.txt | basename→ 错!basename 无参数时会读 stdin,但输入是完整路径,结果仍是完整路径(basename “/data/file.txt” 输出 file.txt,但管道写法易混淆且多余) - ❌
stat -C "%n" $(basename /path/to/file)→ 错!basename 先执行,stat 实际查的是当前目录下同名文件,路径语义丢失 - ❌ 用 %N(带引号的文件名)代替 %n —— %N 用于 -L 且含引号,更不适合自动化提取


















