侧边栏壁纸
博主头像
一笑痕

人生若只如初见,
是可喜亦或者是可悲?

  • 累计撰写 259 篇文章
  • 累计收到 7 条评论

bash 的 # 和 % 删东西,贪婪与否看几个字符就够了

2026-9-18 / 0 评论 / 97 阅读

bash 的 # 和 % 删东西,贪婪与否看几个字符就够了

bash 的参数展开里,#、%、/ 三家都做「删一段、换一段」的事,各自又分贪婪和不贪婪两个版本。被问得最多的是这几个,按原话排。

差在匹配到多长,一个符号最短

差在匹配到多长。# 是最短匹配,## 是最长匹配。变量是 report-2026-09-18-final.tar.gz 的时候:

===== 准备数据 =====
v = report-2026-09-18-final.tar.gz

===== # 最短匹配:从左往右找到第一个匹配就删 =====
v#*-   -> 2026-09-18-final.tar.gz

===== ## 最长匹配:从左往右一路贪到最后一个匹配 =====
v##*-  -> final.tar.gz

#*- 找到第一个 - 就停,删掉 report-;##*- 一路贪到最后一个 -,把 report-2026-09-18- 全删了。% 和 %% 同理,只是从右往左:

===== % 最短匹配:从右往左找到第一个匹配就删 =====
v%.*   -> report-2026-09-18-final.tar

===== %% 最长匹配:从右往左一路贪到头 =====
v%%.*  -> report-2026-09-18-final

记法一句话:一个符号最短,两个符号最长。

为什么 ${p//\/\//\/} 会把 a//b 折叠成 a/b?

这个问法背后通常是这样的事:拿 p="a//b///c.txt" 做 ${p//'//'/'/'},结果不是少一组斜杠,是一组都没少:

===== 相邻斜杠会折叠:路径长什么样,模式就按长什么样匹配 =====
p          = a//b///c.txt
p//'//'/'/' = a/b//c.txt

想删掉所有重复斜杠、只留一个,得跑两遍,因为第二遍跑完 a/b//c.txt 里还剩一组。更直接的办法是换成 sed 或者参数展开的通配写法,一次到位。

模式整体命中,命中多长换多长

模式是按 glob 整体命中的,命中多长就换掉多长:

===== 替换与删除都要求模式整体命中,命中多长换多长 =====
w            = path/name//name/file
w//name/X    = path/X//X/file
w//'//'/'/'  = path/name/name/file

w//name/X 把两处 name 都换成了 X,中间那组斜杠没动,因为模式里没写它。想要的是「把 name 连同前后多余斜杠一起收掉」,模式就得写成 w//'/*name/*'/'X' 这类把斜杠算进去的写法,光盯着 name 本身改不出来。

模式一复杂就别用参数展开

取目录名、取文件名这种事,dirname 和 basename 一直都在,各种边界人家都处理过了。参数展开适合的是简单的掐头去尾:去掉扩展名、去掉前缀。模式一复杂,#、%、/ 混着写出来的东西过两天连作者都读不懂,这种场景宁可多起一个进程换可读性。

    🤞 分享