Wie binde ich mv command nach find command ein?

61

Ich suche AAAmit dem folgenden Befehl nach Dateien, deren Namen in ihrem Pfad enthalten sind :

find path_A -name "*AAA*"

In Anbetracht der vom obigen Befehl angezeigten Ausgabe möchte ich diese Dateien beispielsweise in einen anderen Pfad verschieben path_B. Kann ich den Befehl optimieren, indem ich diese Dateien direkt nach dem Befehl find verschiebe, anstatt sie einzeln zu verschieben?

huahsin68
quelle

Antworten:

87

Mit GNU MV :

find path_A -name '*AAA*' -exec mv -t path_B {} +

Damit wird die -execOption find verwendet, die die Option {}find nacheinander durch jedes Suchergebnis ersetzt und den von Ihnen angegebenen Befehl ausführt. Wie erklärt in man find:

   -exec command ;
          Execute  command;  true  if 0 status is returned.  All following
          arguments to find are taken to be arguments to the command until
          an  argument  consisting of `;' is encountered.  

In diesem Fall verwenden wir die +Version von -exec, um so wenig mvOperationen wie möglich auszuführen :

   -exec command {} +
          This  variant  of the -exec action runs the specified command on
          the selected files, but the command line is built  by  appending
          each  selected file name at the end; the total number of invoca‐
          tions of the command will  be  much  less  than  the  number  of
          matched  files.   The command line is built in much the same way
          that xargs builds its command lines.  Only one instance of  `{}'
          is  allowed  within the command.  The command is executed in the
          starting directory.
cuonglm
quelle
Vergiss nicht das "\;" am ende des exec!
Charles Roth
@ Charles Roth: Es ist +das, was den Job schmälert, Sie können mein Zitat oben oder man findstattdessen lesen
cuonglm
Danke dafür! Ich habe versucht, zu verwenden, -exec mv {} path_b +und es schlug mit Berechtigungsfehlern fehl. TBH, ich verstehe immer noch nicht warum, aber es -exec mv -t path_b {} +ist ein Vergnügen!
Jeremy Davis
6
@JeremyDavis Bei der Verwendung -exec ... {} +, der {}hat das letzte , was vor dem sein +. Deshalb benutzt er mv -t destdir {} +und nicht mv {} destdir +. -exec mv {} destdir ';'Stattdessen wurde eine Erkältung verwendet , die jedoch mvfür jede Datei einmal ausgeführt wurde .
Kusalananda
23

Sie könnten auch so etwas tun.

find path_A -name "*AAA*" -print0 | xargs -0 -I {} mv {} path_B

Wo,

  1. -0Wenn Leerzeichen oder Zeichen (einschließlich Zeilenumbrüche) vorhanden sind, funktionieren viele Befehle nicht. Diese Option berücksichtigt Dateinamen mit Leerzeichen.
  2. -IErsetzen Sie das Vorkommen von replace-str in den Anfangsargumenten durch Namen, die aus der Standardeingabe gelesen wurden. Außerdem werden Eingabeelemente nicht durch Leerzeichen in Anführungszeichen beendet. Stattdessen ist das Trennzeichen das Zeilenumbruchzeichen.

Testen

Ich habe zwei Verzeichnisse als sourcedirund angelegt destdir. Jetzt habe ich eine Reihe von Dateien in sourcediras file1.bak, file2.bakand erstelltfile3 with spaces.bak

Nun habe ich den Befehl wie folgt ausgeführt:

find . -name "*.bak" -print0 | xargs -0 -I {} mv {} /destdir/

Nun, innerhalb dem destdir, wenn ich das tue ls, ich konnte sehen , dass die Dateien von verschoben haben sourcedirzu destdir.

Verweise

http://www.cyberciti.biz/faq/linux-unix-bsd-xargs-construct-argument-lists-utility/

Ramesh
quelle
22

Zum Nutzen von OS X-Benutzern, die auf diese Frage stoßen, unterscheidet sich die Syntax in OS X geringfügig. Angenommen, Sie möchten nicht rekursiv in folgenden Unterverzeichnissen suchen path_A:

find path_A -maxdepth 1 -name "*AAA*" -exec mv {} path_B \;

Wenn Sie alle Dateien rekursiv durchsuchen möchten, gehen Sie wie folgt vor path_A:

find path_A -name "*AAA*" -exec mv {} path_B \;
Mannykary
quelle
Dies ist keine OS X-spezifische Syntax, sondern die gleiche, die findich verwendet habe. Gute Punkte: -maxdepth(besonders wenn path_B ein Unterverzeichnis ist - vermeidet mves, Dateien zu verschieben, die bereits dort sind!) Und benutze \; (also muss {} nicht der letzte Parameter sein und es mvkann die normale Syntax verwendet werden)
drevicko
6

Das -execist der beste Weg, dies zu tun. Wenn dies aus irgendeinem Grund nicht möglich ist, können Sie die Ergebnisse auch in einer Schleife lesen:

find path_A -name "*AAA*" -print0 | 
    while IFS= read -r -d $'\0' file; do mv "$file" path_B; done

Das ist der sichere Weg, es kann mit Dateinamen umgehen, die Leerzeichen, Zeilenumbrüche oder andere seltsame Zeichen enthalten. Ein einfacherer Weg, der jedoch fehlschlägt, wenn Ihre Dateinamen nicht nur aus einfachen alphanumerischen Zeichen bestehen , ist

mv $(find path_A -name "*AAA*") path_B

Aber benutze die while-Schleife.

terdon
quelle
Der einfachere Weg kann immer noch scheitern, wenn your file names consist only of simple alphanumeric characterszBARG_MAX