Korrigieren Sie die Dateierweiterungen

16

Ich habe ungefähr 12000 Bilder von verschiedenen Dateitypen, aber jeder von ihnen wurde in * .jpg umbenannt.

Jetzt möchte ich ihnen ihre richtigen Erweiterungen zurückgeben, wie kann ich das tun?

akabhirav
quelle
2
rekursiv oder in einem "flachen" Verzeichnis?
Jacob Vlijm
1
@steeldriver ziemlich nah dran, aber diese Dateien don nicht eine Erweiterung haben, hier haben sie die falsche Verlängerung.
Jacob Vlijm
1
@JacobVlijm deshalb habe ich die Frage nicht als Duplikat gekennzeichnet: Die in den Antworten vorgeschlagenen Methoden haben hier jedoch Wert, IMHO
steeldriver
@steeldriver da stimme ich voll und ganz zu.
Jacob Vlijm

Antworten:

23

Mit bash geht das relativ einfach:

for f in *jpg; do 
    type=$(file -0 -F" " "$f" | grep -aPo '\0\s*\K\S+') 
    mv "$f" "${f%%.*}.${type,,}"  
done

Dies ist die gleiche Idee wie bei der Antwort von @ AB, aber stattdessen werden Shell-Globs verwendet find. Das ${f%%.*}ist der Dateiname ohne Erweiterung. Mit -0dem fileBefehl of wird \0nach dem Dateinamen ein Ausdruck erstellt, den wir dann für grepden Dateityp verwenden. Dies sollte mit beliebigen Dateinamen funktionieren, einschließlich solcher, die Leerzeichen, Zeilenumbrüche oder andere Elemente enthalten. Dies ${type,,}ist ein Trick, um Erweiterungen in Kleinbuchstaben zu erhalten. Es würde konvertieren PNGzu png.

Sie haben es in Ihrer Frage nicht gesagt, aber wenn Sie möchten, dass dies rekursiv ist und in Unterverzeichnisse abfällt, können Sie stattdessen Folgendes verwenden:

shopt -s globstar
for f in **/*jpg; do 
    type=$(file -0 -F" " "$f" | grep -aPo '\0\s*\K\S+') 
    mv "$f" "${f%%.*}.${type,,}"  
done

Das shopt -s globstarwird bash globstar Option aktivieren , die lässt **Spiel Verzeichnisse:

globstar

Wenn festgelegt, stimmt das in einem Pfadnamen-Erweiterungskontext verwendete Muster ** mit allen Dateien und null oder mehr Verzeichnissen und Unterverzeichnissen überein. Wenn dem Muster ein / folgt, stimmen nur Verzeichnisse und Unterverzeichnisse überein.

terdon
quelle
@AB siehe Update. Es ermöglicht **das Wiederauffinden von Unterverzeichnissen.
Terdon
Diese Semikolons am Ende jeder Zeile sind überflüssig, nicht wahr?
Paddy Landau
@PaddyLandau Ja, ich habe es als Einzeiler getestet und hier aus Gründen der Übersichtlichkeit neue Zeilen hinzugefügt. Ich habe vergessen, sie zu entfernen. Beachten Sie, dass sie nicht falsch sind, nur redundant, wie Sie sagen.
Terdon
Großartig, obwohl filenicht immer die angegebene Erweiterung angegeben wird: Es wird zum Beispiel eine Bash-Datei in diese Datei umgewandelt foo.bourne-again!
Campa
1
@Campa nein natürlich nicht. Es würde auch gefälschte Erweiterungen zu Binärdateien, normalen Textdateien, Perl- und Python-Skripten hinzufügen und die Liste geht weiter. Die Frage bezog sich speziell auf Bilder, und diese haben in der Regel den gleichen Namen wie ihre üblichen Erweiterungen. Denken Sie daran, dass Erweiterungen unter Linux optional sind, mit sehr wenigen Ausnahmen, die eigentlich nichts bewirken. Sie helfen dem Benutzer beim Organisieren seiner Daten, das Betriebssystem kümmert sich nicht um sie.
Terdon
11

Das folgende Skript kann verwendet werden, um eine falsch festgelegte Erweiterung (rekursiv) .jpgin die richtige Erweiterung umzubenennen . Falls eine nicht lesbare Datei gefunden wird, wird dies in der Ausgabe des Skripts gemeldet.

Das Skript das verwendet imghdrModul, die folgenden Arten erkennen: rgb, gif, pbm, pgm, ppm, tiff, rast, xbm, jpeg, bmp, png. Mehr zum imghdrModul hier . Die Liste kann, wie im Link erwähnt, um weitere Typen erweitert werden.

Wie es ist, benennt es Dateien mit der Erweiterung .jpg, wie in der Frage erwähnt , speziell um. Mit einer geringfügigen Änderung kann eine Erweiterung oder ein bestimmter Satz von Erweiterungen in die richtige Erweiterung (oder ohne Erweiterung, wie hier ) umbenannt werden.

Das Drehbuch:

#!/usr/bin/env python3
import os
import imghdr
import shutil
import sys

directory = sys.argv[1]

for root, dirs, files in os.walk(directory):
    for name in files:
        file = root+"/"+name
        # find files with the (incorrect) extension to rename
        if name.endswith(".jpg"):
            # find the correct extension
            ftype = imghdr.what(file)
            # rename the file
            if ftype != None:
                shutil.move(file, file.replace("jpg",ftype))
            # in case it can't be determined, mention it in the output
            else:
                print("could not determine: "+file)

Wie benutzt man

  1. Kopieren Sie das Skript in eine leere Datei und speichern Sie es unter rename.py
  2. Führen Sie es mit folgendem Befehl aus:

    python3 /path/to/rename.py <directory>
    
Jacob Vlijm
quelle
+1 für einfach und leicht zu lesen, im Gegensatz zu bashbasierten Lösungen.
Davide
3

Hinweis: Mein Ansatz scheint zu komplex zu sein. Ich würde es vorziehen, wenn Terdons an Ihrer Stelle antworten.


Mit dem Befehl können Sie fileden Dateityp bestimmen:

% file 20050101_14-24-37_330.jpg 
20050101_14-24-37_330.jpg: JPEG image data, EXIF standard 2.2, baseline, precision 8, 1200x1600, frames 3

% file test.jpg
test.jpg: PNG image data, 1192 x 774, 8-bit/color RGBA, non-interlaced

Mit diesen Informationen können die Dateien umbenannt werden:

Bitte führen Sie einen Test durch, bevor Sie den Befehl auf Ihre Bilder anwenden

find . -type f -iname "*.jpg" -print0 | xargs -0 -I{} file -F"<separator>" {} | 
 awk -F " image data" '{print $1}' | 
  awk -F"<separator> " '{
   system("mv \""$1"\" $(dirname \""$1"\")/$(basename -s .jpg \"" $1 "\")."$2)
   }'

Beispiel

% find . -type f -name "*.jpg"
./test.jpg
./sub/20050101_14-24-37_330.jpg

% find . -type f -iname "*.jpg" -print0 | xargs -0 -I{} file -F"<separator>" {} | awk -F " image data" '{print $1}' | awk -F"<separator> " '{system ("mv \""$1"\" $(dirname \""$1"\")/$(basename -s .jpg \"" $1 "\")."$2)}'

% find . -type f -iname "*"    
./test.PNG
./sub/20050101_14-24-37_330.JPEG
AB
quelle
Beachten Sie, dass dies in dem unwahrscheinlichen Fall, dass einer der Dateinamen Zeilenumbrüche enthält, zum Erliegen kommt.
Terdon
@terdon Ja, ich habe nachgedacht. Leider habe ich keine Ahnung, was ich tun kann. Kannst du helfen?
AB
Ich habe keine Ahnung, wie man das richtig mit awk macht. Es ist nicht das richtige Werkzeug für den Job. Verwenden Sie entweder find -exec bash -c "..."alles, was darin enthalten ist, oder verwenden Sie while read -d '' name type, um den Dateinamen und die fileAusgabe zu teilen und dann zu analysieren $type, um den Dateityp zu ermitteln. Das ist es nicht wirklich wert, siehe meine Antwort, wie man es viel einfacher in reinem (ish) bash macht.
Terdon