Ausschließen eines Verzeichnisnamens in einem rekursiven zsh-Glob

7

Ich verwende zsh unter Linux unter setopt extended_glob ksh_glob glob_dots. Ich suche nach etwas, das einfach über die Befehlszeile ohne Portabilitätsanforderungen eingegeben werden kann. Ich betrachte einen Quellcodebaum ohne „seltsame“ Dateinamen (z. B. nein \in Dateinamen, kein Dateiname beginnend mit -).

Mit einem der folgenden Befehle wird die Liste der Unterverzeichnisse des aktuellen Verzeichnisses rekursiv gedruckt:

find -type d
print -l **/*/

Dies ist eigentlich eine SVN-Kasse:

$ find -type d
./.deps
./.svn
./.svn/text-base
./.svn/prop-base
./.svn/tmp
./.svn/tmp/text-base
./.svn/tmp/prop-base
./.svn/tmp/props
./.svn/props
./lib/.svn
./lib/.svn/text-base
./lib/.svn/prop-base
./lib/.svn/tmp
./lib/.svn/tmp/text-base
./lib/.svn/tmp/prop-base
./lib/.svn/tmp/props
./src/.svn
./src/.svn/text-base
./src/.svn/prop-base
./src/.svn/tmp
./src/.svn/tmp/text-base
./src/.svn/tmp/prop-base
./src/.svn/tmp/props

Ich möchte die .svnVerzeichnisse und ihre Unterverzeichnisse ausschließen, die in jedem Verzeichnis vorhanden sind. Es ist einfach mit find:

find -type d -name .svn -prune -o -print

Kann ich das mit einem kurzen zsh glob machen? Das Ignorieren von Punktdateien kommt nahe (ich muss es explizit tun, weil ich glob_dotsgesetzt habe):

print -l **/*(/^D)

Dies ist jedoch nicht zufriedenstellend, da es das .depsVerzeichnis verbirgt , das ich sehen möchte. Ich kann die Pfade herausfiltern, die Folgendes enthalten .svn:

print -l **/*~(*/|).svn(|/*)(/)

Aber das ist kaum kürzer als find(also wofür benutze ich zsh?). Ich kann es verkürzen print -l **/*~*.svn*(/), aber das filtert auch aufgerufene Verzeichnisse heraus hello.svn. Außerdem durchläuft zsh die .svnVerzeichnisse, was bei NFS oder Cygwin etwas langsam ist.

Gibt es eine bequeme (wie einfach zu tippende) Möglichkeit, einen bestimmten Verzeichnisnamen (oder noch besser: ein beliebiges Muster) in einem rekursiven Glob auszuschließen?

Gilles 'SO - hör auf böse zu sein'
quelle
Warum sollten Sie einige Zeit damit verbringen, herauszufinden, ob Ihre Dateien Striche oder Backslashes haben oder nicht (und andere Leser selbst herausfinden lassen, wenn sie dies nicht garantieren können), anstatt nur das kugelsichere Richtige wie printf '%s\n' **/*oder zu schreiben print -rl -- **/*?
Stéphane Chazelas
@StephaneChazelas In einem Skript sicher. In der Befehlszeile lohnt es sich jedoch, 4 Zeichen zu speichern. In meiner realen Situation musste ich mich nicht über Dateinamen informieren, die ich bereits kannte (Build-Bäume haben normalerweise zahme Dateinamen).
Gilles 'SO - hör auf böse zu sein'

Antworten:

20

Die erweiterten Glob-Operatoren von Zsh unterstützen Matching Over /(im Gegensatz zu Kshs sogar in der Implementierung von Zsh ). Zsh's **/ist eine Abkürzung für (*/)#( */0 oder mehrmals wiederholt). Alles was ich tun muss, ist das *durch ^.svn(alles andere als .svn) zu ersetzen .

print -l (^.svn/)#

Ordentlich!

Gilles 'SO - hör auf böse zu sein'
quelle
4
Dies ist eine großartige Antwort - obwohl dies erforderlich ist setopt EXTENDED_GLOB. Ohne es wirst du bekommen zsh: bad pattern: [...].
Andy Fowler
Ich habe Probleme, Ihre Antwort auf meinen Anwendungsfall anzuwenden. Ich versuche, die CMakeLists.txtin verschiedenen Unterverzeichnissen verteilten Dateien abzugleichen, wobei alle Verzeichnisse, die mit cmake-*der Suche beginnen, ausgeschlossen werden. Ich habe es versucht (^cmake-*)**/CMakeLists.txt#; es funktioniert, aber es enthält nicht das CMakeLists.txtin meinem Arbeitsverzeichnis (dh es findet diejenigen, die sich ausschließlich in Unterverzeichnissen befinden ). Wie kann ich es aufnehmen? Danke und tolle Antwort!
Brainplot
@brainplot Wenn Sie cmake-*auf einer beliebigen Ebene ausschließen möchten, verwenden Sie wie in meiner Antwort (^cmake-*/)#CMakeLists.txt. Wenn Sie nur ausschließen möchten, cmake-foo/bar/CMakeLists.txtaber nicht baz/cmake-qux/CMakeLists.txt, benötigen Sie einen anderen Ansatz : **/CMakeLists.txt~cmake-*/*.
Gilles 'SO - hör auf böse zu sein'
@ Gilles Alle cmake-*Ordner, die ich ausschließen wollte, befinden sich in meinem aktuellen Verzeichnis und ich wollte sie alle ausschließen. Ich habe Ihre erste Lösung ausprobiert und es hat funktioniert. Vielen Dank!
Brainplot
2

Während ksh93Globbing zshauch mit der globstarOption bei weitem nicht annähernd möglich ist ksh93, kann es erreicht werden mit:

set -o globstar
FIGNORE='@(.|..|.svn)'
printf '%s\n' **/
Stéphane Chazelas
quelle