Corregir extensiones de archivo

15

Tengo alrededor de 12000 imágenes de diferentes tipos de archivos, pero cada una de ellas pasó a llamarse * .jpg.

Ahora quiero devolverles sus extensiones adecuadas, ¿cómo puedo hacerlo?

akabhirav
fuente
2
recursivamente, o en un directorio "plano"?
Jacob Vlijm
1
@steeldriver bastante cerca, pero esos archivos no tienen una extensión, aquí tienen la extensión incorrecta .
Jacob Vlijm
1
@JacobVlijm es por eso que no marqué la pregunta como un duplicado: sin embargo, los métodos propuestos en las respuestas tienen valor aquí, en mi humilde opinión
steeldriver
@steeldriver Estoy completamente de acuerdo.
Jacob Vlijm

Respuestas:

22

Puedes hacerlo con relativa facilidad en bash:

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

Esta es la misma idea que la respuesta de @ AB pero usando globos de shell en lugar de find. El ${f%%.*}es el nombre de archivo sin su extensión. El -0del filecomando permite imprimir un \0después del nombre del archivo que luego utilizamos para grepel tipo de archivo. Esto debería funcionar con nombres de archivos arbitrarios, incluidos aquellos que contienen espacios, líneas nuevas o cualquier otra cosa. El ${type,,}es un truco para obtener extensiones en minúsculas. Se convertiría PNGa png.

No dijo en su pregunta, pero si necesita que esto sea recursivo y descienda a subdirectorios, puede usar esto en su lugar:

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

El shopt -s globstarpermitirá opción Globstar de fiesta que permite a **los subdirectorios de los partidos:

Globstar

Si se establece, el patrón ** utilizado en un contexto de expansión de nombre de ruta coincidirá con todos los archivos y cero o más directorios y subdirectorios. Si el patrón es seguido por /, solo coinciden los directorios y subdirectorios.

terdon
fuente
@AB ver actualización. Permite **recurrir a subdirectorios.
terdon
Esos puntos y comas al final de cada línea son redundantes, ¿no?
Paddy Landau
@PaddyLandau sí, lo estaba probando como un trazador de líneas y agregué nuevas líneas para mayor claridad aquí. Olvidé eliminarlos. Tenga en cuenta que no están equivocados, solo redundantes como usted dice.
terdon
Genial, aunque fileno siempre especifica la extensión que parece: ¡está convirtiendo un archivo bash foo.bourne-againaquí, por ejemplo!
Campa
1
@Campa no, por supuesto que no. También agregaría extensiones falsas a archivos binarios, archivos de texto normales, scripts de Perl y Python y la lista continúa. La pregunta era sobre imágenes específicamente y esas tienden a tener el mismo nombre que sus extensiones habituales. Recuerde que las extensiones en Linux son opcionales, con muy pocas excepciones, en realidad no hacen nada. Ayudan al usuario a organizar sus datos, el sistema operativo no se preocupa por ellos.
terdon
11

El siguiente script se puede usar para cambiar el nombre (recursivamente) de una extensión establecida incorrectamente .jpgpor la correcta. En caso de que encuentre un archivo ilegible, lo informará en la salida del script.

La secuencia de comandos utiliza el imghdrmódulo, para reconocer los siguientes tipos: rgb, gif, pbm, pgm, ppm, tiff, rast, xbm, jpeg, bmp, png. Más sobre el imghdrmódulo aquí . La lista se puede ampliar con más tipos, como se menciona en el enlace.

Tal como está, renombra específicamente los archivos con la extensión .jpg, como se menciona en la pregunta. Con un cambio menor, se puede cambiar el nombre de cualquier extensión, o un conjunto específico de extensiones, a la correcta (o sin extensión, como aquí ).

La secuencia de comandos:

#!/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)

Cómo utilizar

  1. Copie el script en un archivo vacío, guárdelo como rename.py
  2. Ejecútelo con el comando:

    python3 /path/to/rename.py <directory>
    
Jacob Vlijm
fuente
+1 para soluciones simples y fáciles de leer, a diferencia de las soluciones basadas en bash.
Davide
3

Nota: mi enfoque parece ser demasiado complejo. Preferiría que los terdons respondan en su lugar.


Puede usar el comando filepara determinar el tipo de archivo:

% 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

Con esta información, los archivos pueden renombrarse:

Haga una prueba antes de aplicar el comando a sus imágenes

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)
   }'

Ejemplo

% 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
fuente
Tenga en cuenta que esto se romperá en el caso poco probable de que alguno de los nombres de archivo contenga nuevas líneas.
terdon
@terdon Sí, he estado pensando. Lamentablemente no tengo idea de lo que puedo hacer. ¿Puede usted ayudar?
AB
No tengo idea de cómo hacer esto correctamente usando awk. No es la herramienta adecuada para el trabajo. Use find -exec bash -c "..."y haga todo lo que esté allí o use while read -d '' name typepara dividir el nombre y la filesalida del archivo y luego analizar $typepara obtener el tipo de archivo. Realmente no vale la pena, vea mi respuesta sobre cómo hacerlo mucho más fácilmente en puro (ish) bash.
terdon