So benennen Sie mehrere Dateien mit find um


37

Ich möchte mehrere Dateien (file1 ... filen to file1_renamed ... filen_renamed) mit folgendem Befehl umbenennen find:

find . -type f -name 'file*' -exec mv filename='{}' $(basename $filename)_renamed ';'

Aber diesen Fehler bekommen:

mv: cannot stat ‘filename=./file1’: No such file or directory

Dies funktioniert nicht, da der Dateiname nicht als Shell-Variable interpretiert wird.

Antworten:


46

Das Folgende ist eine direkte Lösung Ihres Ansatzes:

find . -type f -name 'file*' -exec sh -c 'x="{}"; mv "$x" "${x}_renamed"' \;

Dies ist jedoch sehr teuer, wenn Sie viele übereinstimmende Dateien haben, da Sie mvfür jede Übereinstimmung eine neue Shell starten (die a ausführt ). Und wenn Sie lustige Zeichen in einem Dateinamen haben, wird dies explodieren. Ein effizienterer und sichererer Ansatz ist der folgende:

find . -type f -name 'file*' -print0 | xargs --null -I{} mv {} {}_renamed

Es hat auch den Vorteil, mit seltsam benannten Dateien zu arbeiten. Wenn es findunterstützt wird, kann dies auf reduziert werden

find . -type f -name 'file*' -exec mv {} {}_renamed \;

Die xargsVersion ist nützlich, wenn Sie nicht verwenden {}, wie in

find .... -print0 | xargs --null rm

Hier rmwird einmalig (oder mit vielen Dateien mehrmals) aufgerufen, aber nicht für jede Datei.

Ich habe die basenamein dir stehende Frage entfernt, weil es wahrscheinlich falsch ist: Du würdest foo/bar/file8zu file8_renamed, nicht foo/bar/file8_renamed.

Bearbeitungen (wie in den Kommentaren vorgeschlagen):

  • findOhne gekürzt hinzugefügtxargs
  • Sicherheitsaufkleber hinzugefügt

In dem Fall xist das nutzlos: find . -type f -name 'file*' -exec mv {} "{}_renamed" \; xargsVersion haben die gleiche Effizienz wie das erste Beispiel /
— Costas

Das xgibt es nur, um die Ansprache des Fragestellers direkt zu beheben.
— Thomas Erker

2
(1) Die {}direkte Verwendung in einem shell ( sh -c "…") -Befehl ist sehr gefährlich - Sie sollten ihn immer als Argument übergeben. (2) Nicht alle Versionen findunterstützen das {}_renamedKonstrukt. (3) Ich verstehe Ihre Aussage nicht, xargsdie zum Entfernen von Dateien nützlich ist (im Gegensatz zum Umbenennen).
— G-Man sagt "Monica wiedereinstellen"

@ G-Man: Der Unterschied mit xargsist nicht mvvs. rm, sondern mit {}vs. ohne. Ersteres ähneltmv file1 file1_renamed; mv file2 file2_renamed dem letzteren rm file1 file2.
— Thomas Erker

1
und wenn Sie dann die _renamed-Dateien wieder auf ihren ursprünglichen Namen ändern wollten, wie würden Sie das tun?
— mcmillab

17

Nachdem ich die erste Antwort ausprobiert und ein wenig damit gespielt hatte, stellte ich fest, dass dies etwas kürzer und weniger komplex sein kann -execdir:

find . -type f -name 'file*' -execdir mv {} {}_renamed ';'

Sieht so aus, als sollte es auch genau das tun, was Sie brauchen.


2
Mit findImplementierungen, die dies unterstützen -execdirund {}nicht als Ganzes, ist dies auch die sicherste. Sie können eine hinzufügen möchten -izu , mvobwohl (und -Twenn Ihr mvunterstützt)
— Stéphane Chazelas

1
@ StéphaneChazelas noch besser, anstatt sich auf mveine Eingabeaufforderung zu verlassen, oder zusätzlich dazu können Sie (ohne Zweifel abhängig davon, ob Ihre Implementierung dies findunterstützt) auch verwenden, -okdirwelches den auszuführenden Befehl vor der Ausführung ausgibt.
— Matijs

Erwähnenswert ist, dass dies -depthauch eine gute Idee ist, wenn Sie auch Verzeichnisnamen berühren.
— Ciro Santilli am

Argh, nur festgestellt, dass -execdirhat eine sehr ärgerliche Kehrseite, findweigert sich, etwas zu tun, wenn PATHrelative Pfade enthält ... askubuntu.com/questions/621132/… find: The relative path XXX is included in the PATH environment
— Ciro Santilli am

6

Ein anderer Ansatz ist die Verwendung einer while readSchleife über dem findAusgang. Dies ermöglicht den Zugriff auf jeden Dateinamen als Variable, die bearbeitet werden kann, ohne sich um zusätzliche Kosten / potenzielle Sicherheitsprobleme beim Erstellen eines separaten sh -cProzesses mit kümmern zu müssenfind der -execOption von erstellt wird.

find . -type f -name 'file*' |
    while IFS= read file_name; do
        mv "$file_name" "${file_name##*\/}_renamed"
    done

Und wenn die verwendete Shell die -dOption zur Angabe eines readBegrenzers unterstützt, können Sie Dateien mit seltsamen Namen (z. B. mit einem Zeilenumbruch) folgendermaßen unterstützen:

find . -type f -name 'file*' -print0 |
    while IFS= read -d '' file_name; do
        mv "$file_name" "${file_name##*\/}_renamed"
    done

3

Ich möchte auf die erste Antwort eingehen und beachten, dass dies beim Anhängen an den Dateinamen nicht funktioniert, da das Pfadpräfix im Dateinamenargument ./vorhanden ist.

Wenn ich die Antwort von Thomas Erker ändere, finde ich, dass dies ein allgemeinerer Ansatz ist

find . -name PATTERN -printf "%f\0" | xargs --null -I{} mv {} "prefix {} suffix"

Optionen für xargs:

--nullGibt an, dass jedes übergebene Argument stdinmit einem Nullzeichen ( \0) endet . Auf diese Weise kann der Dateiname Leerzeichen enthalten, andernfalls wird jedes Wort als ein anderer Parameter für die bedrohtmv Befehl .

-I replace-strJedes Auftreten von replace-strwird durch das Argument von ersetzt stdin. Sie können es also für eine andere Zeichenfolge ändern, wenn Sie dies benötigen.


Warum machen die pringf "%f\0"statt a print0?
— SLM

1
@sim, weil Präfixe -print0erzeugt ./werden, wenn es PATTERNShell-Metazeichen enthält, die beim Umbenennen in etwas stören, das den ursprünglichen Namen vorangestellt ist . (zB umbenennen 0 - foo.txtin 00 - foo.txt, 1 - bar.txtnach 01 - bar.txtusw.)
— das-g

2

Ich konnte etwas ähnliches mit dem tun for, findund mv.

for i in $(find . -name 'config.yml'); do mv $i $i.bak; done

Dadurch werden alle config.ymlDateien gefunden und in umbenanntconfig.yml.bak


Dies hat den Vorteil, dass eine variable Ausdehnung verwendet werden kann, z. $ {i: r}. Gute Antwort.
— Salami

Ja, Sie können so ziemlich jeden Ausdruck in das foreinfügen und eine Massenoperation ausführen.
— Varun Risbud
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.