Umbenennen von Dateinamen - Ersetzen von Unterstrichen durch Leerzeichen - in einem Shell-Befehlszeilenskript

7

Ich versuche, alle Dateien in einem Ordner umzubenennen, wobei Unterstriche durch Leerzeichen ersetzt werden.

i.e. this_is_a_test --> this is a test

aber irgendwie vermassle ich das Zitat

> for file in * ; do echo mv -v $file $(echo $file | sed 's/_/\\ /g') ; done
mv -v this_is_a_test this\ is\ a\ test

das sieht in Ordnung aus, aber wenn ich das 'Echo' entferne, mvbeschwert man sich, als ob die Backslashes entfernt worden wären

> for file in * ; do mv -v $file $(echo $file | sed 's/_/\\ /g') ; done
mv: target test is not a directory

Kann jemand auf den Fehler meiner Wege hinweisen?

ottotts
quelle

Antworten:

4

Es gibt einen kleinen Fehler. Verwenden Sie "$newfile"statt nur $newfile. Sie müssen verwenden"

Hier ist der richtige.

for file in * ; do mv -v "$file" "$(echo $file | sed 's/_/\\ /g')" ; done

Wenn Sie einen Dateinamen this_is_a_testhaben, wird die Datei in umbenannt this\ is\ a\ test.

Für den Fall, dass Sie die Datei umbenennen möchten this is a test. Verwenden Sie den folgenden Code:

for file in * ; do mv -v "$file" "$(echo $file | sed 's/_/ /g')" ; done

Es wird empfohlen, ""beim Schreiben eines guten Shell-Skripts Variablen zu verwenden .

Souravc
quelle
1
Alte Antwort, jedoch nur aus Gründen der Klarheit, da sie wieder populär wird, wird dies Dateinamen mit Zeilenumbrüchen brechen
kos
7

Verwenden von rename:

rename -n 's/_/ /g' *

Wenn alles in Ordnung ist, entfernen Sie den -nSchalter:

rename 's/_/ /g' *
~/tmp$ tree
.
├── file
├── file_1
├── file_2
├── file_3
└── file_with_underscores

0 directories, 5 files
~/tmp$ rename 's/_/ /g' *
~/tmp$ tree
.
├── file
├── file 1
├── file 2
├── file 3
└── file with underscores

0 directories, 5 files
kos
quelle
Die Frage bekommt eine ungewöhnliche Popularität =) +1
AB
Ich mag Ihren Baumbefehl… Installed =)
AB
Gilt nur für das Perl- renameDienstprogramm. Funktioniert nicht für die Umbenennung in util-linux.
Lindhe
Warum ist das Umbenennen auf MacOS nicht verfügbar, obwohl es auf Linux basiert?
Agile Bean
3

Stattdessen mv -v "$file" "$(echo $file | sed 's/_/\\ /g')"können Sie einen viel einfacheren Befehl verwenden mv "$f" "${f//_/ }".

So fügen Sie es in Ihr Skript ein:

for f in * ; do mv "$f" "${f//_/ }" ; done

Und das ist es.

Maythux
quelle
Dies ist ziemlich in Ordnung, aber es wird Dateinamen mit Zeilenumbrüchen brechen
kos
1
@kos nein wird es nicht. Shell Globs können problemlos mit Zeilenumbrüchen umgehen. Souravacs Antwort wird in den Zeilenumbrüchen brechen, aber nur wegen sed.
Terdon
@terdon Ich dachte, es hätte die Erweiterung unterbrochen, ähnlich wie Souravacs Antwort auf den Ersatz, das stimmt. @ Maythux Entschuldigung, +1 dann
kos
2

Verwenden Sie den Befehl findund rename:

find <your_start_folder> -type f -regex ".*_+.*" -exec rename 's/_/ /g' {}  \;

Dieser Befehl benennt alle Dateien mit einem _im Dateinamen rekursiv um.

Erläuterung

  • -regex ".*_+.*"

    Suchen Sie alle Dateien mit mindestens einer _im Dateinamen

  • _

    Ersetzen Sie alle Vorkommen von _

  • … Mit einem Leerzeichen ( )


Beispiel

% ls -Rog
.:
total 4
drwxrwxr-x 2 4096 Jun 15 17:39 foo
-rw-rw-r-- 1    0 Jun 15 17:34 foo_bar

./foo:
total 0
-rw-rw-r-- 1 0 Jun 15 17:32 foo_bar

% find . -type f -regex ".*_+.*" -exec rename 's/_/ /g' {} \;

% ls -Rog                                                                 
.:
total 4
drwxrwxr-x 2 4096 Jun 15 17:40 foo
-rw-rw-r-- 1    0 Jun 15 17:34 foo bar

./foo:
total 0
-rw-rw-r-- 1 0 Jun 15 17:32 foo bar
AB
quelle
Hmmm, das funktioniert nur, wenn der Dateiname maximal zwei Unterstriche enthält.
Kos
@ Kos DOH Ich werde alt
AB
@kos ok, fest =)
AB
1
+1 für eine rekursive Art, jedoch gilt das OP-Skript auch für Verzeichnisse. Auch -regexwird hier nicht wirklich gebraucht, -name "*_*"ist genug
kos
0

Hier ist eine awk + for-Loop-Version:

for filename in *; do NEWNAME=$(echo "$filename" | awk '{gsub("_"," "); print}'); mv "$filename" "$NEWNAME";done

Und hier ist es in Aktion

$ ls                                                                                                                                                  

$ echo "TEST" | tee {foo,bar}_{yolo,swag}_{whatever,else}.txt                                                                                         
TEST

$ ls
bar_swag_else.txt      bar_yolo_else.txt      foo_swag_else.txt      foo_yolo_else.txt
bar_swag_whatever.txt  bar_yolo_whatever.txt  foo_swag_whatever.txt  foo_yolo_whatever.txt

$ for filename in *; do NEWNAME=$(echo "$filename" | awk '{gsub("_"," "); print}'); mv "$filename" "$NEWNAME";done                                    

$ ls
bar swag else.txt      bar yolo else.txt      foo swag else.txt      foo yolo else.txt
bar swag whatever.txt  bar yolo whatever.txt  foo swag whatever.txt  foo yolo whatever.txt

Um Zeilenumbrüche und eine mögliche Umbenennung von Verzeichnissen zu berücksichtigen, finden Sie hier das typische IFS + find + read + while-Schleifenkonstrukt. Führen Sie nach dem Vorschlagen des Vorschlags zuerst die Version mit aus find . -type d -print0. Wenn Sie zuerst mit dem Umbenennen von Dateien beginnen und ein Unterverzeichnis einen Unterstrich enthält, wird der Dateiname nicht geändert. Kurz gesagt, kümmern Sie sich zuerst um Verzeichnisse und Unterverzeichnisse und dann um die Dateien

$ ls
file_bar_swag.txt  file_bar_yolo.txt  file_test_swag.txt  file_test_yolo.txt  foo_bar_swag.txt  foo_bar_yolo.txt  foo_test_swag.txt  foo_test_yolo.txt  tester_dir

$ ls tester_dir/                                                                                                                                                                                         
ber_sweg_elze.txt  ber_sweg_whut.txt  ber_yelo_elze.txt  ber_yelo_whut.txt  fee_sweg_elze.txt  fee_sweg_whut.txt  fee_yelo_elze.txt  fee_yelo_whut.txt

$  find . -type d -print0 | while IFS="" read -r -d "" filename ; do NEWNAME=$(echo "$filename" | awk '{gsub("_"," "); print}'); mv "$filename" "$NEWNAME";done 
mv: ‘.’ and ‘./.’ are the same file

$ ls
file_bar_swag.txt  file_bar_yolo.txt  file_test_swag.txt  file_test_yolo.txt  foo_bar_swag.txt  foo_bar_yolo.txt  foo_test_swag.txt  foo_test_yolo.txt  tester dir

$  find . -type f -print0 | while IFS="" read -r -d "" filename ; do NEWNAME=$(echo "$filename" | awk '{gsub("_"," "); print}'); mv "$filename" "$NEWNAME";done                                          
mv: ‘./.yolo and ‘./.yolo are the same file

$ ls
file bar swag.txt  file bar yolo.txt  file test swag.txt  file test yolo.txt  foo bar swag.txt  foo bar yolo.txt  foo test swag.txt  foo test yolo.txt  tester dir

Wie Sie sehen, gibt es ein kleines Problem: Da der Befehl find auch das aktuelle Verzeichnis auflistet (symbolisiert mit einem.), Erstellt der Befehl ein Nebenprodukt - eine Datei mit einem führenden Punkt.

Das Hinzufügen, ! -path .wenn Sie mit Verzeichnissen arbeiten, löst das Problem

$ find . ! -path .  -type d -print0 | while IFS="" read -r -d "" filename ; do NEWNAME=$(echo "$filename" | awk '{gsub("_"," "); print}'); mv "$filename" "$NEWNAME";done                                

$ find .  -type f -print0 | while IFS="" read -r -d "" filename ; do NEWNAME=$(echo "$filename" | awk '{gsub("_"," "); print}'); mv "$filename" "$NEWNAME";done                                          

$ ls
file bar swag.txt  file bar yolo.txt  file baz swag.txt  file baz yolo.txt  foo bar swag.txt  foo bar yolo.txt  foo baz swag.txt  foo baz yolo.txt  tester dir

$ ls "tester dir"
file bar.txt  file baz.txt  foo bar.txt  foo baz.txt

$ ls -a
.  ..  file bar swag.txt  file bar yolo.txt  file baz swag.txt  file baz yolo.txt  foo bar swag.txt  foo bar yolo.txt  foo baz swag.txt  foo baz yolo.txt  tester dir
Sergiy Kolodyazhnyy
quelle