Wie kann ich mit einer Liste von Pfaden Unterverzeichnisse der zuvor genannten Pfade herausfiltern?

7

Angenommen, ich habe eine sortierte Liste absoluter Pfade, wie in meiner Antwort hier (gekürzt und modifiziert für diese Frage):

/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

Ich möchte diese Liste reduzieren, indem alle Pfade entfernt werden, die Unterverzeichnisse der zuvor genannten Pfade sind. Das heißt, für die gegebene Eingabe möchte ich diese Ausgabe:

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

Wie kann dies zB mit Bash leicht in der Befehlszeile erfolgen, sed, awkoder andere gemeinsame Instrumente? Kurze Lösungen, die in eine Zeile passen, werden geschätzt, sind aber nicht erforderlich.

Byte Commander
quelle

Antworten:

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

Die Funktionsweise ist einfach genug, aber die Reihenfolge der Befehle ist wichtig. Wir beginnen damit, die erste Zeile aufzuzeichnen und auszudrucken. Wir gehen zur folgenden Zeile und prüfen, ob die nächste Zeile vorherigen Text enthält. Wenn ja, tun wir nichts. Wenn nicht, ist das ein anderer, neuer Weg.

Der ursprüngliche Ansatz war fehlerhaft und schlug fehl, wenn benachbarte Pfade mit demselben führenden Teilstring wie /var/zomgund vorhanden waren /var/zomgkthx(Dank an Chai T.Rex für den Hinweis). Der Trick besteht darin, "/" an den alten Pfad anzuhängen, um dessen Ende zu kennzeichnen, wodurch die Teilzeichenfolge unterbrochen wird. Der gleiche Ansatz wird in der folgenden Python-Alternative verwendet.

Python-Alternative

#!/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,'')

Probelauf:

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

Dieser Ansatz ähnelt awk-one. Die Idee ist dieselbe: Zeichnen Sie die erste Zeile auf und drucken und setzen Sie die Verfolgungsvariable nur dann weiter, wenn wir auf eine Zeile stoßen, die keine Verfolgungsvariable als Startzeichenfolge hat.

Alternativ könnte einmal auch die os.path.commonprefix()Funktion verwendet werden.

#!/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
quelle
Der awkBefehl scheint für /zomg\n/zomgkthxdie zweite Zeile /zomgkthxfehlzuschlagen , ist kein Unterverzeichnis von /zomg, aber er wird entfernt.
Chai T. Rex
@ ChaiT.Rex, das ist eine der Fallstricke, die ich im letzten Absatz erwähnt habe. Ich sehe leider noch keine Möglichkeit, es zu verbessern.
Sergiy Kolodyazhnyy
@ ChaiT.Rex Ich habe das in meiner Python-Version implementiert. Bitte sehen Sie die aktualisierte Antwort.
Sergiy Kolodyazhnyy
@ ChaiT.Rex hat es noch weiter verbessert. Jetzt sollte es für Pfade mit variabler Länge funktionieren
Sergiy Kolodyazhnyy
8

Eine weitere Python-Version, die die neue pathlibBibliothek verwendet:

#! /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
quelle