388 Stimmen

Wie kann ich beim Mustervergleich in einer Unix/Linux-Shell inverse oder negative Platzhalter verwenden?

Angenommen, ich möchte den Inhalt eines Verzeichnisses kopieren, ohne Dateien und Ordner, deren Namen das Wort "Musik" enthalten.

cp [exclude-matches] *Music* /target_directory

Was sollte anstelle von [exclude-matches] verwendet werden, um dies zu erreichen?

8voto

James M. Lay Punkte 1925

Ein Trick, den ich hier noch nicht gesehen habe und der nicht mit extglob , find ou grep ist es, zwei Dateilisten als Mengen zu behandeln und "diff" sie mit comm :

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

comm ist vorzuziehen gegenüber diff weil es keinen zusätzlichen Ballast hat.

Dies gibt alle Elemente der Menge 1 zurück, ls , die no auch in Satz 2, ls *Music* . Dies setzt voraus, dass die beiden Sätze sortiert sind, damit sie richtig funktionieren. Kein Problem für ls und Glob-Expansion, aber wenn Sie etwas wie find aufzurufen, müssen Sie sort .

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

P

7voto

mipadi Punkte 377834

Sie können auch ein recht einfaches for Schleife:

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

5voto

Ich persönlich bevorzuge die Verwendung von grep und dem Befehl while. Damit kann man leistungsfähige, aber dennoch lesbare Skripte schreiben und sicherstellen, dass man am Ende genau das tut, was man will. Außerdem kann man mit einem echo-Befehl einen Probelauf durchführen, bevor man die eigentliche Operation ausführt. Zum Beispiel:

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

druckt die Dateien aus, die Sie am Ende kopieren werden. Wenn die Liste korrekt ist, ersetzen Sie im nächsten Schritt einfach den Befehl echo durch den Befehl copy wie folgt:

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

3voto

Daniel Bungert Punkte 218

Eine Lösung für dieses Problem 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

Find bietet eine ganze Reihe von Optionen, mit denen Sie ziemlich genau festlegen können, was Sie ein- und ausschließen wollen.

Edit: Adam hat in den Kommentaren angemerkt, dass dies rekursiv ist. Die Suchoptionen mindepth und maxdepth können nützlich sein, um dies zu kontrollieren.

3voto

zrajm Punkte 1267

In den folgenden Werken sind alle *.txt Dateien im aktuellen Verzeichnis, außer denen, die mit einer Nummer beginnen.

Dies 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. In Zeile eins das Muster /some/dir/*.txt wird die for Schleife, um über alle Dateien in /some/dir deren Name mit .txt .

  2. In Zeile zwei wird eine Case-Anweisung verwendet, um unerwünschte Dateien auszusortieren. - Die ${FILE##*/} Ausdruck entfernt alle führenden dir-Namensbestandteile aus dem Dateinamen (hier /some/dir/ ), so dass Muster nur mit dem Basisnamen der Datei übereinstimmen können. (Wenn Sie nur Dateinamen anhand von Suffixen aussortieren, können Sie dies zu $FILE stattdessen).

  3. In Zeile drei werden alle Dateien, die der case Muster [0-9]* ) wird übersprungen (die Zeile continue Anweisung springt zur nächsten Iteration der for Schleife). - Wenn Sie wollen, können Sie hier noch etwas Interessantes tun, z.B. alle Dateien überspringen, die nicht mit einem Buchstaben (a-z) beginnen, indem Sie [!a-z]* oder Sie können mehrere Muster verwenden, um mehrere Arten von Dateinamen zu überspringen, z. B. [0-9]*|*.bak zum Überspringen von Dateien sowohl .bak Dateien und Dateien, die nicht mit einer Nummer beginnen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X