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

仙人之下我无敌,
仙人之上一换一。

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

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

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

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

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

${v#-} 和 ${v##-} 到底差在哪?

差在匹配到多长。# 是最短匹配,## 是最长匹配。变量是 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 本身改不出来。

路径处理什么时候别用参数展开?

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

    🤞 分享