有一个路径列表,我如何过滤掉前面提到的路径的子目录?

假设我有一个绝对路径的排序列表,就像我在这里回答的那样(为这个问题缩短和修改):

/proc /proc/sys/fs/binfmt_misc /proc/sys/fs/binfmt_misc /run /run/cgmanager/fs /run/hugepages/kvm /run/lock /run/user/1000 /run/user/1000/gvfs /tmp /home/bytecommander/ramdisk 

我想要的是通过消除前面提到的路径的子目录的所有路径来减少此列表。 这意味着,对于给定的输入,我想要这个输出:

 /proc /run /tmp /home/bytecommander/ramdisk 

如何使用例如Bash, sedawk或任何其他常用工具在命令行中轻松完成此操作? 适合一行的简短解决方案是值得赞赏的,但不是必需的。

AWK

 $ awk -F '/' 'oldstr && NR>1{ if($0!~oldstr"/"){print $0;oldstr=$0}};NR == 1{print $0;oldstr=$0}' paths.txt /proc /run /tmp /home/bytecommander/ramdisk /var/zomg /var/zomgkthx /zomg /zomgkthx 

这种方式很简单,但命令的顺序很重要。 我们首先记录第一行是什么并将其打印出来。 我们转到以下行,检查下一行是否包含以前的文本。 如果是的话 – 我们什么都不做。 如果没有 – 这是一条不同的新路径。

当相邻路径具有相同的前导子串时,原始方法存在缺陷并失败,例如/var/zomg/var/zomgkthx (感谢Chai T.Rex指出这一点)。 诀窍是将“/”附加到旧路径以表示它的结束,从而破坏子串。 在下面的python替代中使用相同的方法。

Python替代品

 #!/usr/bin/env python import sys,os oldline = None with open(sys.argv[1]) as f: for index,line in enumerate(f): path = line.strip() if index == 0 or not line.startswith(oldline): print(path) oldline = os.path.join(path,'') 

样品运行:

 $ ./reduce_paths.py paths.txt /proc /run /tmp /home/bytecommander/ramdisk /var/zomg /var/zomgkthx /zomg /zomgkthx 

这种方法类似于awk-one。 想法是一样的:记录第一行,并且只有当我们遇到没有跟踪变量的行作为起始子串时才继续打印和重置跟踪变量。

或者,也可以使用os.path.commonprefix()函数。

 #!/usr/bin/env python import sys,os oldline = None with open(sys.argv[1]) as f: for index,line in enumerate(f): path = line.strip() if index == 0 or os.path.commonprefix([path,oldline]) != oldline: print(path) oldline = os.path.join(path,'') 

另一个Python版本,使用新的pathlib库:

 #! /usr/bin/env python3 import pathlib, sys seen = set() for l in sys.stdin: p = pathlib.Path(l.strip()) if not any(x in seen for x in p.parents): seen.add(p) print(str(p))