Tendo uma lista de caminhos, como posso filtrar subdiretórios de caminhos mencionados anteriormente?

7

Digamos que eu tenha uma lista ordenada de caminhos absolutos, como o da minha resposta aqui (encurtado e modificado para esta pergunta):

/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

O que eu quero é reduzir essa lista eliminando todos os caminhos que são subdiretórios dos caminhos mencionados anteriormente. Isso significa que, para a entrada fornecida, eu quero essa saída:

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

Como isso pode ser feito facilmente na linha de comando usando, e. Bash, sed , awk ou qualquer outra ferramenta comum? Soluções curtas que se encaixam em uma linha são apreciadas, mas não são necessárias.

    
por Byte Commander 26.12.2016 / 01:40

2 respostas

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

A maneira como isso funciona é bastante simples, mas a ordem dos comandos é significativa. Começamos gravando o que é a primeira linha e imprimindo. Vamos para a linha seguinte e verificamos se a próxima linha contém texto anterior. Se isso acontecer - não fazemos nada. Se isso não acontecer, esse é um novo caminho diferente.

A abordagem original foi falha e falhou quando havia caminhos adjacentes com a mesma subseqüência principal, como /var/zomg e /var/zomgkthx (Obrigado ao Chai T.Rex por apontar isso). O truque é acrescentar "/" ao caminho antigo para significar o final dele, quebrando assim a substring. A mesma abordagem é usada na alternativa em Python abaixo.

Alternativa em 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,'')

Execução da amostra:

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

Essa abordagem é semelhante ao awk-one. Idéia é a mesma coisa: registre a primeira linha e continue imprimindo e redefinindo a variável de rastreamento somente quando encontrarmos uma linha que não tenha uma variável de rastreamento como substring inicial.

Alternativamente, uma vez pode usar a função os.path.commonprefix() também.

#!/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,'')
    
por Sergiy Kolodyazhnyy 26.12.2016 / 02:19
8

Outra versão do Python, usando a nova biblioteca 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))
    
por muru 26.12.2016 / 02:29