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

Byt*_*der 7 command-line scripts paths text-processing

假设我有一个绝对路径的排序列表,就像我在这里的答案中的那个(针对这个问题进行了缩短和修改):

/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
Run Code Online (Sandbox Code Playgroud)

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

/proc
/run
/tmp
/home/bytecommander/ramdisk
Run Code Online (Sandbox Code Playgroud)

这怎么可能使用如猛砸,命令行很容易做到sedawk或任何其它常见的工具?适合在一行中的简短解决方案是值得赞赏的,但不是必需的。

Ser*_*nyy 10

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
Run Code Online (Sandbox Code Playgroud)

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

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

Python替代品

$ 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
Run Code Online (Sandbox Code Playgroud)

示例运行:

$ ./reduce_paths.py paths.txt                                                                                     
/proc
/run
/tmp
/home/bytecommander/ramdisk
/var/zomg
/var/zomgkthx
/zomg
/zomgkthx
Run Code Online (Sandbox Code Playgroud)

这种方法类似于 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 not line.startswith(oldline):
             print(path)
             oldline = os.path.join(path,'')
Run Code Online (Sandbox Code Playgroud)


mur*_*uru 8

另一个 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))
Run Code Online (Sandbox Code Playgroud)