Ich habe folgende Dateistruktur:
- Irgendein Verzeichnis
- Irgendeine Datei.txt
- Eine weitere Datei hier.log
- Noch eine Datei.mp3
- Ein anderes Verzeichnis
- Mit einer anderen Datei.txt
- Datei auf Stammebene.txt
- Eine weitere Datei auf Stammebene.ext
Was ich jetzt tun möchte, ist, ein kleines Skript auszuführen, das eine andere Datei als Eingabe verwendet, die eine Art Muster/Ersetzungspaare enthält, um diese Dateien rekursiv entsprechend umzubenennen. So dass jedes „another“ (ohne Berücksichtigung der Groß-/Kleinschreibung) durch „foo“ oder jedes „some“ durch „bar“ ersetzt wird.
Ich habe schon vieles ausprobiert, indem ich über Dateien iterierte und die Eingabedatei einlas, aber nichts funktionierte so, wie es sollte, und schließlich habe ich es geschafft, versehentlich mein Testskript zu überschreiben. Aber es waren viele ls
, while
, sed
oder mv
im Einsatz.
Die beiden Dinge, die ich nicht selbst lösen konnte, waren der Umgang mit Leerzeichen in Dateinamen und der Nichtumgang mit Dateien, die bereits bei einer vorherigen Musterübereinstimmung umbenannt wurden.
Vielleicht können Sie mir den richtigen Weg weisen?
Antwort1
TOP="`pwd -P`" \
find . -type d -exec sh -c '
for d
do
cd "$d" && \
find . ! -name . -prune -type f -exec sh -c '\''
while IFS=\; read -r pat repl
do
rename "s/$pat/$repl/g" "$@"
N=$#
for unmoved
do
if [ -f "$unmoved" ]
then
set X ${1+"$@"} "$unmoved"
shift
fi
done
shift "$N"
case $# in 0 ) break ;; esac
done < patterns.csv
'\'' x \{\} +
cd "$TOP"
done
' x {} +
- Richten Sie
find
nur Netzverzeichnisse ein und laden Siesh
sie in einem Rutsch herunter. Dies minimiert die Anzahl der Aufrufe vonsh
. - Richten Sie
find
in jedem dieser Verzeichnisse Netzdateien einregular
, und zwar nur auf einer Tiefe von 1, und geben Sie siesh
in einem Gulp ein. Dadurch wird die Anzahl derrename
Aufrufe des Dienstprogramms minimiert. - Richten Sie eine
while
Schleife ein, um die verschiedenen Paare einzulesenpattern <-> replacement
und auf alle Dateien anzuwendenregular
. - Im Verfahren
rename
-ingWir notieren, ob eine Datei nach demrename
Vorgang noch vorhanden war. Wenn wir feststellen, dass eine Datei noch vorhanden ist, bedeutet dies, dass sie aus irgendeinem Grund nicht umbenannt werden konnte und daher in der nächstenpat/repl
Iteration versucht wird. Wenn die Datei hingegen erfolgreich umbenannt wurde, wenden wir die nächstepat/repl
Iteration NICHT auf diese Datei an, indem wir sie aus der Liste der Befehlszeilenargumente entfernen.
Antwort2
rPairs="/tmp/rename_pairs" \
find . -type f -exec sh -c '
while read -r old new; do
rename "s/$old/$new/i" "$@"
done < "$rPairs"
' x {} +
Vorausgesetzt, Ihre Datei mit den Umbenennungspaaren enthält keine Nicht-ASCII-Zeichen und diese Datei wird außerdem außerhalb des Suchpfads abgelegt.
Antwort3
Nach Rakesh Sharmas Antwort bin ich nach ein bisschen weiterem Experimentieren und etwas Schlaf in die richtige Richtung gekommen.
Schließlich kam ich auf das folgende Skript:
#!/bin/bash
while IFS=";" read pattern replacement
do
if [[ ! -z $pattern ]]
then
echo "Checking files for pattern '$pattern'."
find ./files -name "*$pattern*" -type f | while read fpath
do
fname=$(basename "$fpath")
dname=$(dirname "$fpath")
echo " Found file '$fname' in directory '$dname'. Renaming to '${fname/$pattern/$replacement}'."
mv -- "$fpath" "$dname/${fname/$pattern/$replacement}"
done
fi
done < patterns.csv
Es liest die Datei pattern.csv
und durchläuft ihre Zeilen, wobei die Variablen $pattern
und ausgefüllt werden $replacement
. Im zweiten Schritt ./files
werden alle Dateien in einem Verzeichnis gefunden, die dem aktuellen Muster entsprechen. Dies muss getan werden, um zu vermeiden, dass erneut versucht wird, Dateien umzubenennen, wenn ein zweites Muster übereinstimmt, da dies fehlschlagen würde. Schließlich wird nur die Datei selbst umbenannt, nicht die Verzeichnisse, in denen sie enthalten ist, indem Shell-Parameter ersetzt werden.
Was nicht funktioniert, ist das Ersetzen der Übereinstimmungen ohne Berücksichtigung der Groß- und Kleinschreibung, aber damit kann ich leben.
Antwort4
Wichtig zu beachten ist, dass das Durchsuchen des Verzeichnisbaums ein langsamer Prozess ist und daher nur einmal durchgeführt wird. Wir sehen uns zunächst find
nur die Verzeichnisse im Baum an. Und für jedes Verzeichnis suchen wir nach allen regular files
darunter liegenden Verzeichnissen (keine Rekursion hier). Anschließend wenden wir die Umbenennungstransformation auf diese Dateinamen an und notieren uns gleichzeitig, ob dies erfolgreich war oder nicht. Wenn dies erfolgreich war, brechen wir aus der While-Schleife aus und verhindern so, dass das nächste Patt/Repl auf diese Datei angewendet wird.
tempd="`mktemp -d`" \
find . -type d -exec sh -c '
cd "$1" && \
for f in ./*
do
[ -f "$f" ] || continue
while IFS=\; read -r patt repl
do
case $f in
./*"$patt"* )
rename -v "s/$patt/$repl/g" "$f" 2>&1 | tee "$tempd/$f"
case $(< "$tempf/$f") in "$f renamed "* ) break ;; esac ;;
esac
done < /tmp/patterns.csv
done
' {} {} \;