Wie würden Sie ant-Stil patternsets in Python implementieren Gruppen von Dateien zu wählen?

StackOverflow https://stackoverflow.com/questions/161755

  •  03-07-2019
  •  | 
  •  

Frage

Ant hat eine nette Art und Weise Gruppen von Dateien auswählen, die meisten handlich mit ** einem Verzeichnisbaum anzuzeigen. Z.

**/CVS/*            # All files immediately under a CVS directory.
mydir/mysubdir/**   # All files recursively under mysubdir

Weitere Beispiele sind hier zu sehen:

http://ant.apache.org/manual/dirtasks.html

Wie würden Sie dies in Python implementieren, so dass Sie so etwas wie tun könnten:

files = get_files("**/CVS/*")
for file in files:
    print file

=>
CVS/Repository
mydir/mysubdir/CVS/Entries
mydir/mysubdir/foo/bar/CVS/Entries
War es hilfreich?

Lösung

Sobald Sie über einen ** kommen, wirst du durch die gesamte Verzeichnisstruktur rekursiv, so dass ich denke, an diesem Punkt, ist die einfachste Methode, durch das Verzeichnis mit os.walk läuft, konstruiert einen Pfad und dann prüfen, ob sie das Muster übereinstimmt. Sie können sich wahrscheinlich durch etwas wie eine Regex konvertieren:

def glob_to_regex(pat, dirsep=os.sep):
    dirsep = re.escape(dirsep)
    print re.escape(pat)
    regex = (re.escape(pat).replace("\\*\\*"+dirsep,".*")
                           .replace("\\*\\*",".*")
                           .replace("\\*","[^%s]*" % dirsep)
                           .replace("\\?","[^%s]" % dirsep))
    return re.compile(regex+"$")

(Obwohl beachten Sie, dass dies nicht so ist voll funktions - es ist nicht [a-z] Stil glob-Muster zum Beispiel nicht unterstützt, obwohl dies wahrscheinlich hinzugefügt werden könnte). (Die erste \*\*/ Übereinstimmung gibt Fälle wie \*\*/CVS passenden ./CVS abzudecken, sowie mit nur am Heck Übereinstimmen \*\*.)

Doch offensichtlich wollen Sie nicht unter dem aktuell dir durch alles Rekursion, wenn kein ** Musters der Verarbeitung, so dass ich denke, dass Sie einen Zwei-Phasen-Ansatz benötigen. Ich habe nicht versucht, die unterhalb der Umsetzung, und es gibt wahrscheinlich ein paar Sonderfälle, aber ich denke, es sollte funktionieren:

  1. Teilen Sie die Muster auf Ihrem Verzeichnis seperator. dh pat.split('/') -> ['**','CVS','*']

  2. Recurse durch die Verzeichnisse, und für diese Ebene an dem betreffenden Teil des Musters aus. dh. n levels deep -> look at pat[n].

  3. Wenn pat[n] == '**' Schalter auf die obige Strategie:

    • Rekonstruieren Sie das Muster mit dirsep.join(pat[n:])
    • in einem regulären Ausdruck konvertiert mit glob\_to\_regex()
    • Recursively os.walk durch das aktuelle Verzeichnis, den Pfad in Bezug auf die Ebene aufzubauen Sie begann. Wenn der Pfad die Regex übereinstimmt, ergeben sie.
  4. Wenn pat nicht "**" übereinstimmt, und es ist das letzte Element in dem Muster, dann ergibt alle Dateien / Verzeichnisse passende glob.glob(os.path.join(curpath,pat[n]))

  5. Wenn pat nicht "**" übereinstimmt, und es ist nicht das letzte Element in dem Muster, dann für jedes Verzeichnis, zu überprüfen, ob es (mit glob) pat[n] übereinstimmt. Wenn ja, durch sie rekursiv, Tiefe erhöhen (so wird es bei pat[n+1] aussehen)

Andere Tipps

Sorry, das ist eine recht lange Zeit nach der OP. Ich habe gerade ein Python-Paket veröffentlicht, die dies tut genau das - es heißt Formic und es ist auf der zur Verfügung stehenden PyPI cheese . Mit Formic wird Ihr Problem gelöst mit:

import formic
fileset = formic.FileSet(include="**/CVS/*", default_excludes=False)
for file_name in fileset.qualified_files():
    print file_name

Es gibt eine geringe Komplexität: default_excludes. Ameisen-, wie Ant, schließt CVS Verzeichnisse standardmäßig (wie zum größten Teil von Dateien von ihnen zu sammeln für ein Build gefährlich ist), die Standard-Antwort auf die Frage in keine Dateien zur Folge hätte. Einstellen default_excludes = False deaktiviert dieses Verhalten.

os.walk ist dein Freund. Schauen Sie sich das Beispiel in dem Python-Handbuch ( https://docs.python.org/2/library/os .html # os.walk ) und versuchen, etwas von dem zu bauen.

**/CVS/*“ gegen einen Dateinamen übereinstimmen, die Sie erhalten, können Sie etwas tun:

def match(pattern, filename):
    if pattern.startswith("**"):
        return fnmatch.fnmatch(file, pattern[1:])
    else:
        return fnmatch.fnmatch(file, pattern)

In fnmatch.fnmatch "*" passt alles (einschließlich Schrägstriche).

Es gibt eine Implementierung in dem ‚waf‘ Buildsystem Quellcode. http://code.google .com / p / waf / source / browse / trunk / waflib / Node.py? r = 10755 # 471 sein kann, sollte dies in einer Bibliothek der eigenen verpackt werden?

Yup. Ihre beste Wette ist, wie bereits angedeutet, mit ‚os.walk‘ zu arbeiten. Oder schreiben Wrapper um ' glob ' und ‚ fnmatch ‘ Module, vielleicht.

os.walk ist die beste Wahl dafür. Ich habe das Beispiel unten mit .svn, weil ich, dass praktisch hatte, und es funktionierte großartig:

import re

for (dirpath, dirnames, filenames) in os.walk("."):
    if re.search(r'\.svn$', dirpath):
        for file in filenames:
            print file
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top