Ersetzen von Punkten im Dateinamen durch Unterstriche mit Ausnahme der Erweiterung


9

Kann jemand vorschlagen, wie der Dateiname umbenannt werden soll:

head.body.date.txt

Zu:

head_body_date.txt

Gibt es eine einzeilige Anweisung zum Umbenennen in Unix ?


9
Nehmen Sie an, dass dies .tar.gzeine Erweiterung ist? Willkommen auch bei U & L!
EKons

2
Meinen Sie wirklich Unix im Gegensatz zu Linux? Welches Unix?
Terdon

1
Die offensichtliche Antwort ist mv head.body.date.txt head_body_date.txt. Wenn Sie andere Einschränkungen oder echte Beispiele haben, aktualisieren Sie bitte Ihre Frage mit diesen. Derzeit ist unklar, ob sich die Frage auf eine Datei mit diesem expliziten Namen bezieht, auf ein einzelnes Verzeichnis, in dem alle Dateien ein bestimmtes Format haben, oder ob Sie eine Verzeichnishierarchie nach bestimmten Namen durchsuchen und diese ändern möchten.
Kusalananda

Antworten:


9

Durchlaufen Sie die Dateinamen und verwenden Sie die Parametererweiterung für die Konvertierung:

for f in *.*.*.txt; do i="${f%.txt}"; echo mv -i -- "$f" "${i//./_}.txt"; done

Das Parametererweiterungsmuster ${f//./_}ersetzt alle .s durch _s im Dateinamen ( $f).

Das obige führt einen Probelauf durch, um das eigentliche Umbenennen zu ermöglichen. Entfernen Sie echo:

for f in *.*.*.txt; do i="${f%.txt}"; mv -i -- "$f" "${i//./_}.txt"; done

Wenn Sie sich mit einer Erweiterung befassen möchten, nicht nur .txt:

for f in *.*.*.*; do pre="${f%.*}"; suf="${f##*.}"; \
                     echo mv -i -- "$f" "${pre//./_}.${suf}"; done

Nach Überprüfung auf Entfernen echofür tatsächliche Aktion:

for f in *.*.*.*; do pre="${f%.*}"; suf="${f##*.}"; \
                     mv -i -- "$f" "${pre//./_}.${suf}"; done

Allgemein, für eine beliebige Anzahl von Punkten mindestens einen:

for f in *.*; do pre="${f%.*}"; suf="${f##*.}"; \
                 mv -i -- "$f" "${pre//./_}.${suf}"; done

Gibt es einen allgemeinen Weg, wenn es eine beliebige Anzahl von Punkten gibt?
Ciprian Tomoiagă

1
@ CiprianTomoiaga Do:for f in *.*; do ...; done
heemayl

Ich könnte vorschlagen, das *.*Muster im Beispiel gerade zu setzen , da der Code jetzt so aussieht, als würde er nur mit einer festen Anzahl von Punkten funktionieren.
Ilkkachu

@ilkkachu Fair genug, bearbeitet.
Heemayl

Vielen Dank, heemay, es hat bei mir funktioniert. und etwas lernen!
SG

4

mit perlbasiertrename

$ rename -n 's/\.[^.]+$(*SKIP)(*F)|\./_/g' head.body.date.txt 
rename(head.body.date.txt, head_body_date.txt)
  • \.[^.]+$(*SKIP)(*F) Überspringen Sie dieses Muster und suchen Sie nach alternativen Übereinstimmungen
  • |\./_/gErsetzen Sie alle .durch_

Oder mit negativem Lookahead

$ rename -n 's/\.(?![^.]+$)/_/g' head.body.date.txt 
rename(head.body.date.txt, head_body_date.txt)

Sobald dies in Ordnung ist, entfernen Sie die -nOption


3
rename 's/\.(?=[^.]*\.)/_/g' *.txt

Verwendet die Regex-Ersetzung, um alle bis auf die letzte Instanz .im Dateinamen zu finden (nicht erfassender Lookahead) und durch zu ersetzen _. Verallgemeinert, *.*wenn Sie möchten.

(Diese spezielle Version von renamescheint über installiert zu sein util-linux). Ich benutze Ubuntu 12.04(ja, stark veraltete Maschine).


2

bash::

old=head.body.date.txt oldb=${old%.*} olde=${old##*.} ; \
mv -- "$old" "${oldb//./_}.${olde}"

(1) Sie sollten verwenden mv --, um sich vor „alten“ Dateinamen zu schützen, die mit beginnen -. (2) Sie sollten $oldAnführungszeichen ( "$old") eingeben, um sich vor „alten“ Dateinamen zu schützen, die Leerzeichen oder Zeichen enthalten, die für die Shell besonders sind (wie *und ?). (3) Ihre Antwort schlägt in dem etwas pathologischen Fall fehl, in dem die Erweiterung einen Unterstrich enthält: head.body.date.t_xtwird umbenannt in head_body_date_t.xt.
Scott

@ Scott, danke. Der Code wird jetzt gemäß allen drei genannten Elementen angepasst.
Agc

1

Unter Verwendung mv, sedund revin einer Zeile:

mv "head.body.date.txt" "$(echo head.body.date.txt | rev | sed 's/\./_/2g' | rev)"

Wenn Sie es auf alle txtDateien in der aktuellen Wiederholung anwenden möchten , erscheint die Verwendung von Globs aufgrund der Funktionsweise schwierig. mvSie können jedoch eine forSchleife in einem Einzeiler ausführen :

for file in *.txt; do mv "$file" "$(echo $file | rev | sed 's/\./_/2g' | rev)"; done

Etwas länger, aber Sie können mehrere Muster in Ihrer Schleife abgleichen!


2
(1) Arrghhh! Tu es nicht $(ls …); tu es einfach for file in *.txt. (2) Sie sollten mehr Anführungszeichen verwenden : mv "$file" "$(…)".
Scott

Vielen Dank, Scott, ordnungsgemäß notiert und bearbeitet! Ich bin noch ein Jugendlicher in der Schale, bitte seien Sie sanft;)
Valentin B.

Im Nachhinein renameklingt die Verwendung, wie einige andere in ihren Antworten vorgeschlagen haben, jedoch einfacher ...
Valentin B.

-1
 mv head.body.date.txt head_body_date.txt

Es ist so einfach wie das für jeweils eine Datei. Sie geben nicht an, ob dies rekursiv in einem Verzeichnis oder im gesamten Verzeichnis erfolgen soll, sodass dies die bequemste und schnellste Lösung ist.

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.