Verwenden von sed zum Massenumbenennen von Dateien


86

Zielsetzung

Ändern Sie diese Dateinamen:

  • F00001-0708-RG-Biasliuyda
  • F00001-0708-CS-akgdlaul
  • F00001-0708-VF-hioulgigl

zu diesen Dateinamen:

  • F0001-0708-RG-Biasliuyda
  • F0001-0708-CS-akgdlaul
  • F0001-0708-VF-hioulgigl

Shell-Code

Zu testen:

ls F00001-0708-*|sed 's/\(.\).\(.*\)/mv & \1\2/'

Aufführen:

ls F00001-0708-*|sed 's/\(.\).\(.*\)/mv & \1\2/' | sh

Meine Frage

Ich verstehe den sed-Code nicht. Ich verstehe, was der Substitutionsbefehl ist

$ sed 's/something/mv'

meint. Und ich verstehe reguläre Ausdrücke etwas. Aber ich verstehe nicht, was hier passiert:

\(.\).\(.*\)

oder hier:

& \1\2/

Ersteres sieht für mich einfach so aus: "Ein einzelnes Zeichen, gefolgt von einem einzelnen Zeichen, gefolgt von einer beliebigen Längenfolge eines einzelnen Zeichens" - aber es steckt sicherlich noch mehr dahinter. Soweit letzterer Teil:

& \1\2/

Ich habe keine Ahnung.


Antworten:


148

Zunächst sollte ich sagen, dass der einfachste Weg, dies zu tun, darin besteht, die Befehle vor dem Umbenennen oder Umbenennen zu verwenden.

Unter Ubuntu, OSX (Homebrew-Paket rename, MacPorts-Paket p5-file-rename) oder anderen Systemen mit Perl-Umbenennung (Vorname):

rename s/0000/000/ F0000*

oder auf Systemen mit Umbenennung von util-linux-ng wie RHEL:

rename 0000 000 F0000*

Das ist viel verständlicher als der entsprechende sed-Befehl.

Zum Verständnis des Befehls sed ist jedoch die Manpage sed hilfreich. Wenn Sie man sed ausführen und nach & suchen (mit dem Befehl / suchen), finden Sie in s / foo / bar / replace ein Sonderzeichen.

  s/regexp/replacement/
         Attempt  to match regexp against the pattern space.  If success
         ful,  replace  that  portion  matched  with  replacement.    The
         replacement may contain the special character & to refer to that
         portion of the pattern space  which  matched,  and  the  special
         escapes  \1  through  \9  to refer to the corresponding matching
         sub-expressions in the regexp.

Entspricht daher \(.\)dem ersten Zeichen, auf das verwiesen werden kann \1. Dann .passt das nächste Zeichen, das ist immer 0. Dann \(.*\)den Rest des Dateinamens übereinstimmt, die durch Bezug genommen werden kann \2.

Die Ersetzungszeichenfolge fügt alles unter Verwendung &(des ursprünglichen Dateinamens) zusammen und \1\2ist jeder Teil des Dateinamens mit Ausnahme des zweiten Zeichens, das eine 0 war.

Dies ist meiner Meinung nach ein ziemlich kryptischer Weg, dies zu tun. Wenn der Befehl zum Umbenennen aus irgendeinem Grund nicht verfügbar war und Sie sed zum Umbenennen verwenden wollten (oder vielleicht etwas zu Komplexes zum Umbenennen?), Würde eine deutlichere Darstellung in Ihrer Regex die Lesbarkeit erheblich verbessern. Vielleicht so etwas wie:

ls F00001-0708-*|sed 's/F0000\(.*\)/mv & F000\1/' | sh

In der Lage zu sein, zu sehen, was sich in s / search / replace / tatsächlich ändert, macht es viel lesbarer. Außerdem werden keine Zeichen aus Ihrem Dateinamen herausgesaugt, wenn Sie ihn versehentlich zweimal ausführen oder so.


1
Auf meinem RHEL-Server lautet die Umbenennungssyntax "0000 000 F0000 * umbenennen"
David LeBauer

1
Es ist sehr wahrscheinlich, dass renamees sich um einen "umbenannten" Link handelt. dh renamewurde "umbenannt" von prename.. zB in Ubuntu: readlink -f $(which rename)Ausgaben /usr/bin/prename... Das renamevon David erwähnte ist ein völlig anderes Programm.
Peter.O

1
Guter Punkt, Peter. Ich habe die Antwort aktualisiert, um beide Dienstprogramme zum Umbenennen zu adressieren.
Edward Anderson

3
Um dies zu debuggen, entfernen Sie das Rohr am Ende in sh. Die Befehle werden auf dem Bildschirm angezeigt.
Ben Mathews

1
Sind Sie sicher, dass es ein guter Rat ist, zufällige Daten durchzuleiten sh? Dies ist möglicherweise gefährlich, da beliebiger Code ausgeführt werden kann (Sie behandeln Daten als Code).
gniourf_gniourf

44

Sie haben Ihre sed-Erklärung erhalten, jetzt können Sie nur die Shell verwenden, ohne externe Befehle zu benötigen

for file in F0000*
do
    echo mv "$file" "${file/#F0000/F000}"
    # ${file/#F0000/F000} means replace the pattern that starts at beginning of string
done

1
Schön, aber Sie können keine Referenzen mit Klammern machen.
Leonidas Tsampros

26

Ich habe vor ein sedpaar Jahren einen kleinen Beitrag mit Beispielen zum Batch-Umbenennen geschrieben:

http://www.guyrutenberg.com/2009/01/12/batch-renaming-using-sed/

Beispielsweise:

for i in *; do
  mv "$i" "`echo $i | sed "s/regex/replace_text/"`";
done

Wenn die Regex - Gruppen enthält (zB \(subregex\) , dann können Sie sie im Ersetzungstext als verwenden \1\, \2usw.


Beachten Sie, dass von Antworten nur auf Links abgeraten wird (Links werden im Laufe der Zeit veraltet). Bitte bearbeiten Sie Ihre Antwort und fügen Sie hier eine Zusammenfassung hinzu.
Kleopatra

nicht so effizient, erledigt aber die Arbeit für ein paar hundert Dateien. Upvoted.
Varun Chandak

20

Der einfachste Weg wäre:

for i in F00001*; do mv "$i" "${i/F00001/F0001}"; done

oder tragbar

for i in F00001*; do mv "$i" "F0001${i#F00001}"; done

Dies ersetzt das F00001Präfix in den Dateinamen durch F0001. Credits für Mahesh hier: http://www.debian-administration.org/articles/150


3
Sie sollten die Variableninterpolationen richtig zitieren. mv "$i" "${i/F00001/F0001}". Aber +1
Tripleee

7

Der sedBefehl

s/\(.\).\(.*\)/mv & \1\2/

Mittel zu ersetzen:

\(.\).\(.*\)

mit:

mv & \1\2

genau wie ein normaler sedBefehl. Die Klammern &und \nMarkierungen ändern dies jedoch ein wenig.

Die Suchzeichenfolge stimmt mit dem einzelnen Zeichen am Anfang überein (und merkt sich dies als Muster 1), gefolgt von einem einzelnen Zeichen, gefolgt vom Rest der Zeichenfolge (als Muster 2 gespeichert).

In der Ersatzzeichenfolge können Sie auf diese übereinstimmenden Muster verweisen, um sie als Teil der Ersetzung zu verwenden. Sie können sich auch auf den gesamten übereinstimmenden Teil als beziehen &.

Mit diesem sedBefehl wird also ein mvBefehl erstellt, der auf der Originaldatei (für die Quelle) und den Zeichen 1 und 3 basiert und Zeichen 2 (für das Ziel) effektiv entfernt. Sie erhalten eine Reihe von Linien im folgenden Format:

mv F00001-0708-RG-biasliuyda F0001-0708-RG-biasliuyda
mv abcdef acdef

und so weiter.


1
Dies war eine gute Erklärung, aber es kann nützlich sein, darauf hinzuweisen, wie Sie den Befehl sed mit anderen Befehlen verwenden, um die Dateien tatsächlich umzubenennen. Zum Beispiel:ls | sed "s/\(.\).\(.*\)/mv & \1\2/" | bash
Jcarballo

@jcarballo: Es ist gefährlich, eine Shell zu analysieren ls, durchzuleiten sedund dann durchzuleiten ! Es unterliegt einer willkürlichen Codeausführung mit gefälschten Dateinamen. Das Problem ist, dass Daten als Daten behandelt werden sollten. Hier werden sie normalerweise ohne Vorsichtsmaßnahmen in Code serialisiert. Ich wünschte, paxdiablo könnte diese Antwort löschen, da sie wirklich keine gute Praxis zeigt. (Ich bin über diese Frage gestolpert, weil ein Anfänger zufällig | shnach einem Befehl, der nicht funktioniert hat, und nachdem er diese Frage und die Antworten gesehen hatte, dachte, dass es besser funktionieren würde - ich bin entsetzt!) :).
gniourf_gniourf

3

Das Backslash-Paren-Zeug bedeutet: "Halten Sie sich beim Anpassen des Musters an das Zeug, das hier passt." Später können Sie auf der Seite des Ersatztextes diese gespeicherten Fragmente mit "\ 1" (erster Block in Klammern), "\ 2" (zweiter Block) usw. zurückerhalten.


1

Wenn Sie nur das zweite Zeichen entfernen, unabhängig davon, um was es sich handelt, können Sie Folgendes tun:

s/.//2

aber dein Befehl baut a mv Befehl und ihn zur Ausführung an die Shell weiter.

Dies ist nicht lesbarer als Ihre Version:

find -type f | sed -n 'h;s/.//4;x;s/^/mv /;G;s/\n/ /g;p' | sh

Das vierte Zeichen wird entfernt, da findjedem Dateinamen "./" vorangestellt wird.


Ich wünschte, Sie könnten diese Antwort löschen. Während es im speziellen Fall des OP vielleicht gut war, sehen viele Leute Antworten wie diese und verstehen sie nicht und leiten sie zufällig | shnach einem Befehl weiter, der nicht funktioniert, in der Hoffnung, dass es funktioniert besser. Es ist schrecklich! (und außerdem ist das keine gute Praxis). Ich hoffe du wirst verstehen!
gniourf_gniourf

0

Die Klammern erfassen bestimmte Zeichenfolgen, die von den rückwärts gestrichenen Zahlen verwendet werden.


0
 ls F00001-0708-*|sed 's|^F0000\(.*\)|mv & F000\1|' | bash

Schrecklich! vorbehaltlich einer willkürlichen Codeausführung (möglicherweise nicht im spezifischen Kontext der Frage, aber es gibt viele Leute, die solche Antworten sehen und versuchen, zufällig etwas einzugeben, das so aussieht, und es ist beängstigend gefährlich!). Ich wünschte, Sie könnten diese Antwort löschen (außerdem haben Sie hier eine andere gute, die ich positiv bewertet habe).
gniourf_gniourf

0

Folgendes würde ich tun:

for file in *.[Jj][Pp][Gg] ;do 
    echo mv -vi \"$file\" `jhead $file|
                           grep Date|
                           cut -b 16-|
                           sed -e 's/:/-/g' -e 's/ /_/g' -e 's/$/.jpg/g'` ;
done

Wenn das dann in Ordnung aussieht, fügen Sie | shes am Ende hinzu. So:

for file in *.[Jj][Pp][Gg] ;do 
    echo mv -vi \"$file\" `jhead $file|
                           grep Date|
                           cut -b 16-|
                           sed -e 's/:/-/g' -e 's/ /_/g' -e 's/$/.jpg/g'` ;
done | sh

0

Verwenden der Perl-Umbenennung (ein Muss in der Toolbox):

rename -n 's/0000/000/' F0000*

Entfernen Sie den -nSchalter, wenn die Ausgabe gut aussieht, um sie wirklich umzubenennen.

Warnung Es gibt andere Tools mit demselben Namen, die dies möglicherweise können oder nicht. Seien Sie also vorsichtig.

Der Umbenennungsbefehl, der Teil des util-linux Pakets ist, wird dies nicht tun.

Wenn Sie den folgenden Befehl ausführen ( GNU)

$ rename

und du siehst perlexpr , dann scheint dies das richtige Werkzeug zu sein.

Wenn nicht, um es zum Standard (normalerweise bereits der Fall) zu machen Debianund abzuleiten wie Ubuntu:

$ sudo apt install rename
$ sudo update-alternatives --set rename /usr/bin/file-rename

Für Archlinux:

pacman -S perl-rename

Für Distributionen der RedHat-Familie:

yum install prename

Das Paket 'prename' befindet sich im EPEL- Repository.


Für Gentoo:

emerge dev-perl/rename

Für * BSD:

pkg install gprename

oder p5-File-Rename


Für Mac-Benutzer:

brew install rename

Wenn Sie diesen Befehl nicht mit einer anderen Distribution haben, durchsuchen Sie Ihren Paketmanager, um ihn zu installieren, oder führen Sie ihn manuell aus :

cpan -i File::Rename

Die alte Standalone-Version finden Sie hier


Mann umbenennen


Dieses Tool wurde ursprünglich von Larry Wall, dem Vater des Perl, geschrieben.


-1
for i in *; do mv $i $(echo $i|sed 's/AAA/BBB/'); done

4
Willkommen bei SO. Bitte erwägen Sie, eine Erklärung Ihres Codes hinzuzufügen. Es wird anderen Benutzern helfen, es zu verstehen.
Digvijay S

Diese Antwort ist gut, aber es ist eine nahezu doppelte Antwort auf eine hoch bewertete Antwort oben.
Eric Leschinski
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.