Stapelweises Umbenennen von Ordnern mit einem einzigen Bash-Befehl

Stapelweises Umbenennen von Ordnern mit einem einzigen Bash-Befehl

Ich habe Ordner wie folgt eingerichtet:

/path/to/directory/SLUG_1/SLUG_1 - SLUG_2 - SLUG_3

SLUG_2 steht für ein Jahr und kann einen Buchstaben nach dem Jahr enthalten, beispielsweise „1994“ oder „2003a“.

Ich möchte diese Dateien wie folgt umbenennen:

/path/to/directory/SLUG_1/SLUG_2 - SLUG_3

Mit diesem Befehl komme ich dem Ziel ziemlich nahe:

find $root -mindepth 2 -maxdeth 2 -type d | sed "s#\(\(/path/to/directory/[^/]*/\).* - \([0-9]\{4\}[a-bA-B]\? - .*\)\)#mv "\1" "\2\3"#

Dies druckt:

mv "/path/to/directory/SLUG_1/SLUG_1 - SLUG_2 - SLUG_3" "/path/to/directory/SLUG_1/SLUG_2 - SLUG_3"

Das ist genau der Befehl, den ich ausführen möchte. Aber ich kann ihn nicht ausführen.

Ich habe versucht, die Ausgabe einer Variablen zuzuweisen und sie durch Aufrufen der Variablen auszuführen. Das hat nicht funktioniert. Ich habe ein paar Variationen dieser Idee ausprobiert und dabei Fehler bekommen.

Mir kommt es so vor, als ob mir hier ein Werkzeug fehlt. Irgendein iteratives Werkzeug, das diese Arbeit erleichtert. Irgendwelche Vorschläge?

Antwort1

Vorausgesetzt, alle Unterverzeichnisse /path/to/directoryfolgen dieser Namenskonvention:

cd /path/to/directory
prename -n 's~/\d{4}[a-z]? - ~/~i' */*

prenameist derPerl umbenennen(jede der Varianten ist geeignet).

Antwort2

Dies druckt:

mv ...

Das ist genau der Befehl, den ich ausführen möchte. Aber ich kann ihn nicht ausführen.

Ich habe versucht, die Ausgabe einer Variablen zuzuweisen und sie durch Aufrufen der Variablen auszuführen. Das hat nicht funktioniert. Ich habe ein paar Variationen dieser Idee ausprobiert und dabei Fehler bekommen.

Es fühlt sich an, als ob mir hier ein Werkzeug fehlt.

Der einfache Weg, den Sie suchenist, an Ihren Befehl anzuhängen | bash. So gelangen Sie von „ein Befehl, der einen Befehl druckt“ zu „tatsächlichem Ausführen des gedruckten Befehls“.

Obwohl Sie jedoch doppelte Anführungszeichen in den auszudruckenden Befehl eingefügt haben,das ist einschlechte Ideein ein Skript einbinden.

Sie haben sich vielleicht mit dem Aspekt der Leerzeichen befasst, aber was passiert, wenn die Dateinamen direkt Anführungszeichen enthalten? Oder Zeilenumbrüche? Oder Variablennamen (die in Anführungszeichen erweitert werden)?

DernurUnzulässige Zeichen in Dateinamen sind ein Schrägstrich ( /) und ein Nullbyte.

Beim Schreiben von Skripts sollten Sie einen viel höheren Robustheitsstandard einhalten, als dies bei einer interaktiven Befehlszeile der Fall wäre.

In einer Befehlszeile können Sie den Befehl sehen, den Sie ausführen möchten, Sie können seine Richtigkeit bestätigen und ihn dann ausführen. In einem Skript gibt es keine Aufsicht und keine Bestätigung. Das Skript führt aus, was Sie ihm gesagt haben, egal wie destruktiv sich das herausstellen könnte.

Also dasrichtigAnsatz ist eigentlich zu verwenden find, wie in meinemandere Antwort. Dies wirdbeliebigDateinamen korrekt und enthält keine Schwachstellen für die Ausführung willkürlichen Codes.

Antwort3

Eine andere Antwort: Verwendung von GNU findzur Umbenennung. Diese Methode ist robust, unabhängig davon, welche Zeichen im Dateinamen enthalten sein können.

Wenn ich Ihren Anwendungsfall richtig verstehe, möchten Sie Verzeichnisse umbenennen, die mit dem vollständigen Namen ihres übergeordneten Verzeichnisses beginnen. Mit anderen Worten, wenn Ihr Verzeichnis so heißt:

/some/path/abcdefghi/abcdefghi - something - else/

Sie möchten es wie folgt umbenennen:

/some/path/abcdefghi/something - else/

Da Sie GNU als Tag für diese Frage angeben, können Sie die GNU-Erweiterungen für den findBefehl verwenden und dies folgendermaßen handhaben:

find . -mindepth 2 -maxdepth 2 -type d -regextype posix-egrep -regex '.*/([^/]+)/\1[^/]+' -exec sh -c 'new="$(sed -r "s:/([^/]+)/\\1 ?-? ?([^/]+)\$:/\\1/\\2:" <<<$1)"; mv "$1" "$new"' find-sh {} \;

Testergebnisse:

[vagrant@localhost test]$ mkdir -p SLUG_1/SLUG_{1\ -\ SLUG_{2..4}\ -\ SLUG_5,7\ -\ something}
[vagrant@localhost test]$ find . -type d
.
./SLUG_1
./SLUG_1/SLUG_1 - SLUG_2 - SLUG_5
./SLUG_1/SLUG_1 - SLUG_4 - SLUG_5
./SLUG_1/SLUG_1 - SLUG_3 - SLUG_5
./SLUG_1/SLUG_7 - something
[vagrant@localhost test]$ find . -mindepth 2 -maxdepth 2 -type d -regextype posix-egrep -regex '.*/([^/]+)/\1[^/]+' -exec sh -c 'new="$(sed -r "s:/([^/]+)/\\1 ?-? ?([^/]+)\$:/\\1/\\2:" <<<$1)"; mv "$1" "$new"' find-sh {} \;
[vagrant@localhost test]$ find . -type d
.
./SLUG_1
./SLUG_1/SLUG_3 - SLUG_5
./SLUG_1/SLUG_4 - SLUG_5
./SLUG_1/SLUG_2 - SLUG_5
./SLUG_1/SLUG_7 - something
[vagrant@localhost test]$ 

verwandte Informationen