Ich habe Dateien mit derselben Nomenklatur log_<date>.txt
in einem verschachtelten Verzeichnis verstreut root
. Wo <date>
steht das Erstellungsdatum der Dateien im Format JJJJMMTTHHMM? Aus Gründen der Codekompatibilität möchte ich alle Dateien umbenennen in<date>_log.txt
Ich weiß, dass ich den Ausdruck erfassen müsste <date>
, aber wie mache ich das in Bash? Und wie mache ich das in einem verschachtelten Verzeichnis?
Antwort1
Angenommen, Sie haben den Namen einer dieser Dateien in der Shell-Variable name
. Das Entfernen des log_
Bits aus dem Wert der Variable erfolgt dann mit ${name#log_}
, und das Entfernen des .txt
Bits erfolgt mit ${name%.txt}
. Was übrig bleibt, ist das Datum, und in welchem Format es vorliegt, ist im Wesentlichen nicht interessant. Es wäre einfach, den entfernten Wert zu nehmen und _log_.txt
am Ende hinzuzufügen, um den neuen Namen zu erstellen.
Um dies für die Dateien in einem einzigen Verzeichnis zu tun, verwenden Sie eine Schleife. Beachten Sie, dass wir jetzt auch den Verzeichnisnamen für den Anfang des Variablenwerts entfernen müssen.
shopt -s nullglob
for name in root/log_*.txt; do
newname=root/${name#root/log_} # remove prefix, add root/ back
newname=${newname%.txt}_log.txt # remove suffix, add _log.txt back
printf 'Would move "%s" to "%s"\n' "$name" "$newname"
# mv -i "$name" "$newname"
done
Den eigentlichen Befehl habe ich aus Sicherheitsgründen auskommentiert mv
. Du solltest ihn einmal probeweise ausführen, um zu sehen, was zuerst passiert.
Mit der nullglob
Shell-Option wird die bash
Shellentfernennicht übereinstimmende Muster, anstatt sie unerweitert zu lassen. Das bedeutet, dass die Schleife überhaupt nicht ausgeführt würde, wenn sich log_*.txt
im angegebenen Verzeichnis keine Dateien befänden.
Dies kann auf jede solche Datei im aktuellen Verzeichnis (einschließlich des aktuellen Verzeichnisses) rekursiv angewendet werden durchmehr oder wenigerÄndern Sie einfach das Muster, über das wir eine Schleife durchführen:
shopt -s globstar nullglob
for name in ./**/log_*.txt; do
dirpath=${name%/*} # get path of directory
newname=$dirpath/${name#$dirpath/log_} # remove prefix, add directory path back
newname=${newname%.txt}_log.txt # remove suffix, add _log.txt back
printf 'Would move "%s" to "%s"\n' "$name" "$newname"
# mv -i "$name" "$newname"
done
Zu den weiteren Änderungen gehört die Aktivierung der globstar
Shell-Option, mit der wir **
in Unterverzeichnissen nacheinander suchen können. Außerdem wähle ich den Verzeichnispfad aus, um ihn dem neuen Namen voranstellen zu können.
find
Sie können zur Generierung der Eingabe für die Schleife auch Folgendes verwenden :
find . -type f -name 'log_*.txt' -exec sh -c '
for name do
dirpath=${name%/*} # get path of directory
newname=$dirpath/${name#$dirpath/log_} # remove prefix, add directory path back
newname=${newname%.txt}_log.txt # remove suffix, add _log.txt back
printf "Would move \"%s\" to \"%s\"\n" "$name" "$newname"
# mv -i "$name" "$newname"
done' sh {} +
Dadurch werden alle regulären Dateien gefunden, deren Namen log_*.txt
im aktuellen Verzeichnis oder darunter übereinstimmen, und diese werden dann stapelweise an ein Inline- sh -c
Skript übergeben, das im Wesentlichen dieselbe Arbeit ausführen würde wie die Schleife in der zweiten Variante oben.
Antwort2
Am besten verwenden Sie ein Tool zum Umbenennen von Batchdateien wie oder zsh
eine der Perl-basierten Varianten von :zmv
mmv
rename
zsh << 'EOF'
autoload zmv
zmv 'log_(*).txt' '${1}_log.txt'
EOF
Oder
prename 's/log_(.*)\.txt/${1}_log.txt/' log*.txt