Esk*_*ker 5 shell sed awk text-processing
给定一个包含路径排序列表的文本文件,如何删除所有由于父项(立即或不是)也在列表中而冗余的路径?
例如:
/aaa/bbb
/aaa/bbb/ccc
/ddd/eee
/fff/ggg
/fff/ggg/hhh/iii
/jjj/kkk/lll/mmm
/jjj/kkk/lll/mmm/nnn
Run Code Online (Sandbox Code Playgroud)
应该减少到:
/aaa/bbb
/ddd/eee
/fff/ggg
/jjj/kkk/lll/mmm
Run Code Online (Sandbox Code Playgroud)
我试过在 awk 中使用子字符串,但不能保证每次父路径都在同一级别,所以我无法让它工作。
我认为应该这样做。修改输入文件以添加更多案例
$ cat ip.txt
/aaa/bbb
/aaa/bbbd
/aaa/bbb/ccc
/ddd/eee
/fff/ggg
/fff/ggg/hhh/iii
/jjj/kkk/lll/mmm
/jjj/kkk/lll/mmm/nnn
/jjj/kkk/xyz
Run Code Online (Sandbox Code Playgroud)
使用 awk
$ awk '{for (i in paths){if (index($0,i"/")==1) next} print; paths[$0]}' ip.txt
/aaa/bbb
/aaa/bbbd
/ddd/eee
/fff/ggg
/jjj/kkk/lll/mmm
/jjj/kkk/xyz
Run Code Online (Sandbox Code Playgroud)
paths[$0] 是以输入行为键的引用for (i in paths) 每一行都与所有保存的键进行比较if (index($0,i"/")==1) next如果输入行与行首附加的保存键匹配/,则跳过该行
/用于避免/aaa/bbbd匹配/aaa/bbb以及强制性sed解决方案:
sed '1s/^/#/;x;G;\_#\([^#]*\)#.*\n\1/_s/\n.*//;s/\n\(.*\)/\1#/;h;$! d;x;s/^#//;s/#$//;y/#/\n/'
Run Code Online (Sandbox Code Playgroud)
该脚本收集保留空间中的路径。对于每一个新行,保持空间被附加到模式空间以检查它是否已经发生。
此解决方案假定该字符#未在文件中使用。否则使用不同的字符,或者,如果您使用 GNU sed,请使用帖子底部的简短版本。
详细说明:
1s/^/#/
Run Code Online (Sandbox Code Playgroud)
为了可移植性,该#字符用于分隔保留空间中的路径。对于第一行,我们需要以首字母开头#
x;G
By exchanging the spaces and appending the hold space, we have the list of already occured buffers first, then the new path.
\_#\([^#]*\)#.*\n\1/_s/\n.*//
Run Code Online (Sandbox Code Playgroud)
如果\_..._地址匹配,则新路径是先前路径的子路径,因此将其删除。
s/\n\(.*\)/\1#/
Run Code Online (Sandbox Code Playgroud)
我们的空间中仍然有一个换行符,所以路径是新的,我们将它添加到列表中。
h;$! d
Run Code Online (Sandbox Code Playgroud)
如果这不是最后一行,则将新列表保存到保留空间并重新开始。
x;s/^#//;s/#$//;y/#/\n/
Run Code Online (Sandbox Code Playgroud)
对于最后一行,删除#开头和结尾并用#换行符替换另一个。
GNU 的替代品 sed
sed如果您不介意顺序是否恢复,则可以使用 GNU 扩展更紧凑地完成此操作:
sed 'G;\_^\([^\n]*\)/.*\n\1\n_s/[^\n]*\n//;h;$! d;x;s/^\n//;s/\n$//'
Run Code Online (Sandbox Code Playgroud)
解释如上,但使用换行符作为分隔符而不是添加#.