Wie kann ich inverse oder negative Platzhalter bei Musteranpassung in einem Unix / Linux-Shell?

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

Frage

Sagen, dass ich den Inhalt eines Verzeichnisses kopieren möchten Ausschließen von Dateien und Ordner, deren Namen das Wort ‚Musik‘.

cp [exclude-matches] *Music* /target_directory

Was anstelle von [exclude-Treffer] gehen sollte dies zu erreichen?

War es hilfreich?

Lösung

In Bash können Sie es tun, indem Sie die extglob Option aktivieren, so (ersetzen ls mit cp und das Zielverzeichnis hinzufügen, natürlich)

~/foobar> shopt extglob
extglob        off
~/foobar> ls
abar  afoo  bbar  bfoo
~/foobar> ls !(b*)
-bash: !: event not found
~/foobar> shopt -s extglob  # Enables extglob
~/foobar> ls !(b*)
abar  afoo
~/foobar> ls !(a*)
bbar  bfoo
~/foobar> ls !(*foo)
abar  bbar

Sie können später deaktivieren extglob mit

shopt -u extglob

Andere Tipps

Die extglob Shell Option gibt Ihnen leistungsfähigeren Musterabgleich in der Befehlszeile.

Sie es mit shopt -s extglob einschalten, und schalten Sie es aus mit shopt -u extglob.

In Ihrem Beispiel würden Sie zunächst tun:

$ shopt -s extglob
$ cp !(*Music*) /target_directory

Der vollständige verfügbar ext beendet glob bing Operatoren sind (Auszug aus man bash):

  

Wenn die extglob Shell-Option aktiviert wird, um die shopt builtin verwendet wird, mehrere verlängert   Pattern-Matching-Operatoren werden erkannt. In der folgenden Beschreibung wird ein pat   tern-Liste ist eine Liste von einem oder mehreren Mustern durch |. Composite-Muster   kann unter Verwendung eines oder mehrerer der folgenden Teilmuster gebildet werden:

     
      
  • ? (Muster-Liste)
      Null oder ein Vorkommen der gegebenen Muster
  •   
  • * (Muster-Liste)
      Spiele null oder mehr Vorkommen der gegebenen Muster
  •   
  • + (Muster-Liste)
      Spiele ein oder mehrere Vorkommen der gegebenen Muster
  •   
  • @ (Muster-Liste)
      Spiele eines der angegebenen Muster
  •   
  • ! (Muster-Liste)
      Spiele nichts außer einem des gegebenen Musters
  •   

So zum Beispiel, wenn Sie alle Dateien im aktuellen Verzeichnis aufzulisten wollen, die nicht .c oder .h Dateien, würden Sie tun:

$ ls -d !(*@(.c|.h))

Natürlich normale Shell Ballung funktioniert, so das letzte Beispiel auch geschrieben werden:

$ ls -d !(*.[ch])

Nicht in bash (die ich kenne), aber:

cp `ls | grep -v Music` /target_directory

Ich weiß, das ist nicht genau das, was Sie suchen, aber es wird Ihr Beispiel lösen.

Wenn Sie die mem Kosten für die Verwendung mit dem Befehl exec vermeiden wollen, glaube ich Sie mit xargs besser machen können. Ich denke, die folgende ist eine effiziente Alternative zu

find foo -type f ! -name '*Music*' -exec cp {} bar \; # new proc for each exec



find . -maxdepth 1 -name '*Music*' -prune -o -print0 | xargs -0 -i cp {} dest/

In der bash eine Alternative zu shopt -s extglob ist die GLOBIGNORE Variable . Es ist nicht wirklich besser, aber ich finde es leichter zu merken.

Ein Beispiel, das kann sein, was das ursprüngliche Plakat wollte:

GLOBIGNORE="*techno*"; cp *Music* /only_good_music/

Wenn Sie fertig sind, unset GLOBIGNORE der Lage sein, im Quellverzeichnis rm *techno*.

Sie können auch eine ziemlich einfache for Schleife verwenden:

for f in `find . -not -name "*Music*"`
do
    cp $f /target/dir
done

Meine persönliche Präferenz ist grep und den während Befehl zu verwenden. Dies ermöglicht eine leistungsstarke und dennoch lesbar Skripte schreiben sicherzustellen, dass Sie genau am Ende tun, was Sie wollen. Plus durch einen Echo-Befehl können Sie einen Testlauf durchführen, bevor die eigentliche Operation durchgeführt wird. Zum Beispiel:

ls | grep -v "Music" | while read filename
do
echo $filename
done

wird aus den Dateien drucken, die Sie kopieren landen. Wenn die Liste korrekt ist der nächste Schritt besteht darin, einfach den Befehl echo mit dem Kopierbefehl wie folgt zu ersetzen:

ls | grep -v "Music" | while read filename
do
cp "$filename" /target_directory
done

Eine Lösung hierfür kann mit find gefunden werden.

$ mkdir foo bar
$ touch foo/a.txt foo/Music.txt
$ find foo -type f ! -name '*Music*' -exec cp {} bar \;
$ ls bar
a.txt

Suche schon einige Optionen hat, können Sie ziemlich spezifisch bekommen, was Sie sind und ausgeschlossen werden.

Edit: Adam in den Kommentaren darauf hingewiesen, dass dieser rekursiv. finden Optionen mindepth und maxdepth kann bei der Kontrolle dieses nützlich sein.

Ein Trick, den ich hier noch nicht gesehen hat, dass nicht extglob, find oder grep nicht verwendet zwei Dateilisten als Sätze zu behandeln und „diff“ sie mit comm:

comm -23 <(ls) <(ls *Music*)

comm ist vorzuziehen diff, weil es keine zusätzlichen cruft hat.

Dies gibt alle Elemente des Satzes 1, ls, das ist nicht auch in Satz 2, ls *Music*. Dies erfordert beiden Sätze in sortierter Reihenfolge zu sein, richtig zu arbeiten. Kein Problem für ls und glob Expansion, aber wenn Sie so etwas wie find verwenden, müssen Sie sort aufzurufen.

comm -23 <(find . | sort) <(find . | grep -i '.jpg' | sort)

potentiell nützlich.

Das folgende Werk listet alle *.txt Dateien im aktuellen Verzeichnis, mit Ausnahme derjenigen, die mit einer Zahl beginnen.

Das funktioniert in bash, dash, zsh und alle anderen POSIX-kompatiblen Shells.

for FILE in /some/dir/*.txt; do    # for each *.txt file
    case "${FILE##*/}" in          #   if file basename...
        [0-9]*) continue ;;        #   starts with digit: skip
    esac
    ## otherwise, do stuff with $FILE here
done
  1. Im Rahmen eines das Muster /some/dir/*.txt die for Schleife verursacht alle Dateien in /some/dir, dessen Namen mit .txt Ende iterieren.

  2. In Zeile zwei eine Case-Anweisung verwendet wird, unerwünschte Dateien auszusortieren. - Der ${FILE##*/} Ausdruck Streifen jede führend dir name Komponente aus dem Dateinamen aus (hier /some/dir/), so dass prasselt gegen nur den Basisnamen der Datei übereinstimmen kann. (Wenn Sie nur Dateinamen Ausmerzung basierend auf Suffixe, können Sie diese verkürzen, anstatt $FILE.)

  3. In Zeile drei, werden alle Dateien, die case Muster [0-9]*) Linie entsprechen, werden übersprungen werden (die continue Anweisung springt zum nächsten Iteration der for Schleife). - Wenn Sie möchten, können Sie hier etwas Interessanteres tun, zum Beispiel wie alle Dateien übersprungen, die nicht mit einem Buchstaben (a-z) mit [!a-z]* beginnen oder Sie können mehrere Muster verwenden, um verschiedene Arten von Dateinamen zum Beispiel überspringen [0-9]*|*.bak zu überspringen Dateien sowohl .bak-Dateien und Dateien, die nicht mit einer Zahl beginnen.

Dies würde es tun, ohne genau ‚Musik‘

cp -a ^'Music' /target

dies und das für das Ausschließen Dinge wie Musik? * Oder *? Musik

cp -a ^\*?'complete' /target
cp -a ^'complete'?\* /target
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top