如何在Shell环境中移除前缀为上一行的已排序目录行?
反向移除前缀匹配行(Shell环境实现)
嘿,这个需求我熟!刚好和Stack Overflow上那种「移除有相似前缀的重复行」的需求反过来——咱们要做的是:删掉那些被上一行路径作为父目录的行,只保留最上层的路径或者同级的独立路径。
先明确下需求细节:
给定已排序的目录路径列表(每行一个路径,结尾带
/),需要移除所有满足「上一行是当前行前缀」的行。换个说法就是,如果前一行的路径是当前行的父目录,就删掉当前行,留下父目录;如果两行是同级(比如bar/foo/和bar/foo2/),就都保留。
示例输入
a/ a/b/c/ a/d/ bar/foo/ bar/foo2/ c/d/ c/d/e/
预期输出
a/ bar/foo/ bar/foo2/ c/d/
最简洁的Shell解决方案:用awk处理
因为输入已经是排序好的,awk可以逐行对比当前行和上一行的关系,一行命令就能搞定:
awk 'NR==1 || !index($0, prev) {print; prev=$0}' input.txt
拆解命令逻辑:
NR==1:第一行直接打印,同时把第一行内容存到变量prev里,作为后续对比的基准。!index($0, prev):index($0, prev)会返回prev在当前行$0中的起始位置,如果返回0,说明当前行不是以上一行作为前缀,这时候就打印当前行,并且更新prev为当前行。
如果你的输入还没排序?没关系,题目说允许重新排序,先排序再处理就行:
sort input.txt | awk 'NR==1 || !index($0, prev) {print; prev=$0}'
用sed实现的备选方案
要是你更习惯用sed,也可以试试这个写法(逻辑稍微绕一点,但同样有效):
sed -n '1p; N; /^\(.*\)\n\1/p; D' input.txt
sed命令逻辑解释:
1p:先打印第一行,初始化对比基准。N:把下一行读入模式空间,和当前行合并成两行(格式是上一行\n当前行)。/^\(.*\)\n\1/p:如果合并后的内容符合「第一行是第二行的前缀」(正则里的\1引用了第一行的内容),就打印第一行。D:删除模式空间里的第一行,循环处理下一行。
验证效果
把示例输入存成input.txt,运行上面的awk命令,就能直接得到预期的输出啦!
内容的提问来源于stack exchange,提问作者ahmet alp balkan
相关产品推荐
相关产品推荐

