Имея список путей, как я могу отфильтровать подкаталоги ранее упомянутых путей?

7

Предположим, что у меня есть отсортированный список абсолютных путей, например, тот, который в моем ответе здесь (сокращен и изменен для этот вопрос):

/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, sed , awk или любые другие общие инструменты? Короткие решения, которые вписываются в одну строку, оцениваются, но не требуются.

    
задан Byte Commander 26.12.2016 в 00:40
источник

2 ответа

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

Как это работает, достаточно просто, но порядок команд значителен. Начнем с записи первой строки и ее распечатки. Переходим к следующей строке и проверяем, содержит ли следующая строка предыдущий текст. Если это так - мы ничего не делаем. Если это не так - это новый, новый путь.

Исходный подход был ошибочным и не удался, когда были смежные пути с одной и той же ведущей подстрокой, например /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,'')
    
ответ дан Sergiy Kolodyazhnyy 26.12.2016 в 01:19
источник
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))
    
ответ дан muru 26.12.2016 в 01:29
источник

Ознакомьтесь с другими вопросами по меткам