Suchen Sie mehrere Dateien und benennen Sie sie unter Linux um

72

Ich habe Dateien wie a_dbg.txt, b_dbg.txt ...in einem Suse 10System. Ich möchte ein Bash-Shell-Skript schreiben, das diese Dateien umbenennen soll, indem "_dbg" aus ihnen entfernt wird.

Google schlug mir vor, den renameBefehl zu verwenden . Also habe ich den Befehl rename _dbg.txt .txt *dbg*auf dem ausgeführtCURRENT_FOLDER

Mein aktueller CURRENT_FOLDERenthält die folgenden Dateien.

CURRENT_FOLDER/a_dbg.txt
CURRENT_FOLDER/b_dbg.txt
CURRENT_FOLDER/XX/c_dbg.txt
CURRENT_FOLDER/YY/d_dbg.txt

Nach dem Ausführen des renameBefehls

CURRENT_FOLDER/a.txt
CURRENT_FOLDER/b.txt
CURRENT_FOLDER/XX/c_dbg.txt
CURRENT_FOLDER/YY/d_dbg.txt

Es wird nicht rekursiv ausgeführt, wie dieser Befehl zum Umbenennen von Dateien in allen Unterverzeichnissen ausgeführt wird. Like XXund YYich werden so viele Unterverzeichnisse haben, deren Name nicht vorhersehbar ist. Und ich CURRENT_FOLDERwerde auch einige andere Dateien haben.

Rashok
quelle
5
Ich bekommeBareword "..." not allowed while "strict subs" in use at (eval 1) line 1.
Albert Hendriks
2
Warum gibt es nicht NUR -r umbenennen und unser Leben einfacher machen, anstatt umfangreiche Problemumgehungen?
NeverMind9

Antworten:

131

Sie können verwenden find, um alle übereinstimmenden Dateien rekursiv zu finden:

$ find . -iname "*dbg*" -exec rename _dbg.txt .txt '{}' \;

EDIT: was die '{}'und \;sind?

Das -execArgument bewirkt, dass find renamefür jede gefundene übereinstimmende Datei ausgeführt wird. '{}'wird durch den Pfadnamen der Datei ersetzt. Das letzte Token \;dient nur zum Markieren des Endes des exec-Ausdrucks.

Alles, was in der Manpage zu finden ist:

 -exec utility [argument ...] ;
         True if the program named utility returns a zero value as its
         exit status.  Optional arguments may be passed to the utility.
         The expression must be terminated by a semicolon (``;'').  If you
         invoke find from a shell you may need to quote the semicolon if
         the shell would otherwise treat it as a control operator.  If the
         string ``{}'' appears anywhere in the utility name or the argu-
         ments it is replaced by the pathname of the current file.
         Utility will be executed from the directory from which find was
         executed.  Utility and arguments are not subject to the further
         expansion of shell patterns and constructs.
Kamituel
quelle
@kamituel Danke. Ich lerne Linux und habe mich ein paar Mal gefragt, was das bedeutet. danke für die nette erklärung. Können Sie bitte sagen, was -iname tut? und können wir den obigen Befehl (In Centos 7) alsfind . -type f "*dbg*" -exec rename "_dbg.txt" ".txt" * {} \;
Syed Mudabbir
Ich habe es überprüft und es funktioniert nur für Dateien, nicht für Verzeichnisse.
Syed Mudabbir
Ich habe diesen Fehler mehrmals erhalten : Illegal octal digit '9' at (user-supplied code), at end of line. Ich habe sowohl einfache Anführungszeichen als auch doppelte Anführungszeichen und keine Anführungszeichen für die Argumente von ausprobiert rename. Ich versuche , speziell zu ersetzen 0123-MISCmit 0123_AB_MISC. Dies ist mein Befehl find . -iname "*0123-MISC*" -exec rename 0123-MISC 0123_AB_MISC {} \;
Eddy
8
Für diejenigen, die bekommen Bareword "..." not allowed while "strict subs" in use at ..., versuchen Sie:find . -iname "*BUILD*" -exec rename 's/_dbg.txt/.txt/' '{}' \;
jsand
3
Ich bekommefind: rename: No such file or directory
Bartek Pacia
15

Zum rekursiven Umbenennen verwende ich folgende Befehle:

find -iname \*.* | rename -v "s/ /-/g"
Gantan
quelle
Auch nützlich, um Sicherungsdateien wiederherzustellen. find -iname \*.* | rename -v -f "s/\.bak//g"
Paolo
Mein Umbenennen hat -v nicht. Sind Sie unter Mac OS X? Ich bin Ubuntu 16.
Katastic Voyage
-v entspricht --verbose bei meiner Veröffentlichung des Tools 'Umbenennen'; nicht wichtig für das Ergebnis. Bitte geben Sie 'Mann umbenennen' zur weiteren Verdeutlichung ein.
Gantan
1
Dies funktioniert unter Ubuntu 18.10 nicht, da renameversucht wird, den gesamten Pfad umzubenennen, und dies fehlschlägt, wenn ein übergeordnetes Verzeichnis geändert wird. Sie müssen stattdessen nur mit Basisnamen arbeiten, wie unter stackoverflow.com/questions/16541582/… gezeigt, und zuerst mit der Tiefe fortfahren-depth .
Ciro Santilli 25 冠状 病 六四 事件 25
Die Standardeinstellung -f ist auch force, not find
Scott
14

kleines Skript, das ich geschrieben habe, um alle Dateien mit der Erweiterung .txt in die Erweiterung .cpp unter / tmp und Unterverzeichnisse rekursiv zu ersetzen

#!/bin/bash

for file in $(find /tmp -name '*.txt')
do
  mv $file $(echo "$file" | sed -r 's|.txt|.cpp|g')
done
VishnuVardhanA
quelle
7
Gebrochene Methode! bitte nicht tun for file in $(find ...). Siehe Warum Sie die Ausgabe von ls (1) nicht analysieren sollten .
gniourf_gniourf
13

mit bash:

shopt -s globstar nullglob
rename _dbg.txt .txt **/*dbg*
Glenn Jackman
quelle
5
Tolle Lösung. Ich musste diese Bash-Optionen mit aktivieren shopt -s globstar nullstar. Für das Umbenennungs-Tool, das über brew install renameMacOS installiert ist , musste ich tunrename -s oldStuff newStuff **/*
Xander Dunn
4

find -execdir rename

https://stackoverflow.com/a/16541670/895245 funktioniert direkt nur für Suffixe, dies funktioniert jedoch für willkürliche Regex-Ersetzungen für Basisnamen:

PATH=/usr/bin find . -depth -execdir rename 's/_dbg.txt$/_.txt' '{}' \;

oder nur Dateien betreffen:

PATH=/usr/bin find . -type f -execdir rename 's/_dbg.txt$/_.txt' '{}' \;

-execdirZuerst cds in das Verzeichnis, bevor nur der Basisname ausgeführt wird.

Getestet unter Ubuntu 20.04, finde 4.7.0, benenne 1.10 um.

Bequemer und sicherer Helfer dafür

find-rename-regex() (
  set -eu
  find_and_replace="$1"
  PATH="$(echo "$PATH" | sed -E 's/(^|:)[^\/][^:]*//g')" \
    find . -depth -execdir rename "${2:--n}" "s/${find_and_replace}" '{}' \;
)

GitHub stromaufwärts .

Beispiel für die Verwendung von Leerzeichen '' durch Bindestriche '-'.

Trockenlauf, der zeigt, was in was umbenannt werden würde, ohne es tatsächlich zu tun:

find-rename-regex ' /-/g'

Ersetzen Sie:

find-rename-regex ' /-/g' -v

Befehlserklärung

Die fantastische -execdirOption führt im Gegensatz zum cdAusführen eines renameBefehls vor der Ausführung des Befehls ein Verzeichnis durch -exec.

-depth Stellen Sie sicher, dass die Umbenennung zuerst bei Kindern und dann bei Eltern erfolgt, um mögliche Probleme mit fehlenden Elternverzeichnissen zu vermeiden.

-execdir ist erforderlich, da das Umbenennen mit Eingabepfaden ohne Basisnamen nicht gut funktioniert, z. B. schlägt Folgendes fehl:

rename 's/findme/replaceme/g' acc/acc

Das PATHHacken ist erforderlich, da -execdires einen sehr ärgerlichen Nachteil hat: Es findist äußerst eigensinnig und weigert sich, irgendetwas damit zu tun, -execdirwenn Sie relative Pfade in Ihrer PATHUmgebungsvariablen haben, z ./node_modules/.bin.

find: Der relative Pfad './node_modules/.bin' ist in der Umgebungsvariablen PATH enthalten, die in Kombination mit der Aktion -execdir von find unsicher ist. Bitte entfernen Sie diesen Eintrag aus $ PATH

Siehe auch: /ubuntu/621132/why-using-the-execdir-action-is-insecure-for-directory-which-is-in-the-path/1109378#1109378

-execdirist eine GNU-Find- Erweiterung für POSIX . renameist Perl-basiert und kommt aus dem renamePaket.

Benennen Sie die Lookahead-Problemumgehung um

Wenn Ihre Eingabepfade nicht von stammen findoder wenn Sie genug von der relativen Pfadstörung haben, können wir einige Perl-Lookaheads verwenden, um Verzeichnisse wie folgt sicher umzubenennen:

git ls-files | sort -r | xargs rename 's/findme(?!.*\/)\/?$/replaceme/g' '{}'

Ich habe kein passendes Analogon für gefunden -execdir mit xargs: /superuser/893890/xargs-change-working-directory-to-file-path-before-executing/915686

Dies sort -rist erforderlich, um sicherzustellen, dass Dateien nach ihren jeweiligen Verzeichnissen kommen, da längere Pfade nach kürzeren mit demselben Präfix folgen.

Getestet in Ubuntu 18.10.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
quelle
1
Funktioniert bei mir. Diese Lösung sollte höher gewählt werden.
Paul Chris Jones
3

Scipt oben kann in einer Zeile geschrieben werden:

find /tmp -name "*.txt" -exec bash -c 'mv $0 $(echo "$0" | sed -r \"s|.txt|.cpp|g\")' '{}' \;
Manh Tai
quelle
1
@steven versuchen Sie diesen Fund zu verwenden. -name "* .txt" -exec bash -c 'mv $ 0 $ (echo "$ 0" | sed -r "s / .txt / .cpp / g")' '{}' \;
xxbinxx
Das Öffnen / Ausführen einer neuen Shell für jede Datei ist extrem teuer (langsam), daher mache ich das normalerweise nicht.
Binarus
0

Wenn Sie nur umbenennen möchten und nichts dagegen haben, ein externes Tool zu verwenden, können Sie rnm verwenden . Der Befehl wäre:

#on current folder
rnm -dp -1 -fo -ssf '_dbg' -rs '/_dbg//' *

-dp -1 macht es rekursiv zu allen Unterverzeichnissen.

-fo impliziert den Nur-Datei-Modus.

-ssf '_dbg' sucht nach Dateien mit _dbg im Dateinamen.

-rs '/_dbg//' ersetzt _dbg durch eine leere Zeichenfolge.

Sie können den obigen Befehl auch mit dem Pfad des CURRENT_FOLDER ausführen:

rnm -dp -1 -fo -ssf '_dbg' -rs '/_dbg//' /path/to/the/directory
Jahid
quelle
0

Sie können dies unten verwenden.

rename --no-act 's/\.html$/\.php/' *.html */*.html
井上 智 文
quelle
Mein Umbenennen hat nicht --no-act, Ubuntu 16. Sind Sie unter Mac OS X?
Katastic Voyage
ich auch. Unbekannte Option: No-Act unter Ubuntu 17.10. Ich verwende keinen Mac OS. No-Act ist eine Trockenlaufoption.
27 智 文
-3

klassische Lösung:

for f in $(find . -name "*dbg*"); do mv $f $(echo $f | sed 's/_dbg//'); done
Impuls
quelle
4
Das ist völlig kaputt. Bitte tu das nicht.
gniourf_gniourf
~/tmp$ mkdir a && cd a/ && touch 1_dbg.txt && cd .. ~/tmp$ for f in $(find . -name "*dbg*"); do mv $f $(echo $f | sed 's/_dbg//'); done ~/tmp$ cd a/ ~/tmp/a$ l total 8 drwxrwxr-x 2 repu1sion repu1sion 4096 Sep 17 21:17 . drwxrwxr-x 3 repu1sion repu1sion 4096 Sep 17 21:17 .. -rw-rw-r-- 1 repu1sion repu1sion 0 Sep 17 21:17 1.txt
Puls
1
Vielleicht scheint es zu funktionieren, aber es bricht mit Dateinamen, die Leerzeichen oder Glob-Zeichen enthalten. Sie sagen, es ist eine klassische Lösung, aber es ist wahrscheinlicher, dass die Lösung, die Sie gefunden haben, schreckliche Fehler aufweist (es tut mir leid, hart zu sein, aber Ihre Zeile könnte in Lehrbüchern enthalten sein, als Beispiel dafür, was Sie nicht tun sollten; Übung: Zählen Sie die Anzahl der Fehler).
gniourf_gniourf
1
for f in $(find ....): Tu das nicht! es bricht mit Dateinamen, die Leerzeichen oder Glob-Zeichen enthalten; außerdem findhat die -execOption , die sehr wahrscheinlich , stattdessen verwendet werden kann; oder for f in *_dbg*; dooder wenn Sie einen rekursiven Glob wollen, for f in **/*_dbg; domit shopt -s globstar.
gniourf_gniourf
2
Schließlich ist eine viel bessere Lösung : shopt -s globstar nullglob; for f in **/*_dbg*; do mv -- "$f" "${f/_dbg}"; done.
gniourf_gniourf