Renombrar varios archivos desde la línea de comando

Publicado el Miércoles, 12 de enero de 2011

Hace un tiempo en el trabajo un compañero necesitaba renombrar una serie de archivos en Ubuntu. No conocía de primera un comando que permita hacer eso, y después encontré que hay algunas aplicaciones con este fin.

En el momento pensé la solución mas rápida que podía hacer, sin tener que recurrir a fijarme "cómo se hace esto en..." y lo primero que me vino a la cabeza fue el amigo AWK.

No es muy escalable ni reutilizable, pero sirvió en su momento para solucionar el tema. La idea era renombrar imágenes que estaban siendo generadas con la extensión "pl" a la extensión "png". El script en cuestión después de un par de depuraciones quedó así:

ls -l | awk '/pl$/{print $8 }' | awk 'BEGIN{FS="."};{system(" mv "$1 "."  $2 " " $1".png")}'

¿Porqué use ls -l? Necesitaba solamente los archivos cuyo nombre terminara con ".pl", y ls -l lista al final de la línea el nombre de archivo, por lo que pensé en usar la expresión regular 'pl$' (siendo $ el final de línea) además de necesitar una línea por nombre de archivo, para así poder recorrerlos uno por uno con AWK (por lo menos así lo razoné en su momento).

Terminal

El primer awk filtra los resultados, quedándose con cada línea del ls que contuviera archivos cuyo nombre terminara en "pl" e imprimiéndolos.

Esto es atrapado con otro awk que define el punto "." como separador de campo. De esta forma, puedo acceder al "nombre" del archivo al ser la primer parte de la separación ($1), y la extensión como la segunda ($2). Esto suponiendo que los archivos son del tipo nombre.extension, en caso que no lo fueran habría que modificar un poco las cosas...

Por último uso el comando mv del shell mediante system renombrando así cada archivo, reemplazando pl por png. Puede sonar complejo pero fue la forma más intuitiva que pensé para lograr esto.

Seguramente podría transformarlo en un script más útil y reutilizable reimplementándolo con parámetros para los nombres de archivos y el directorio. Probablemente también hayan formas más simples de hacer esto, pero como decía, para mi fue la forma "natural" de hacerlo en su momento 😛

Pensándolo mejor ahora, con Bash y Sed es todavía mas fácil hacerlo:

#!/bin/sh
 
for i in *.pl; do
    j=`echo $i | sed 's/pl/png/'`
    mv $i $j
done

Es raro ver cómo uno idea una solución basada en los lenguajes o tecnologías con los que se siente más a gusto (o más lo divierten). Y es más que interesante ver las soluciones que otros crean para el mismo problema.

Otros posts del estilo me han enseñado varias formas de realizar los mismos trabajos "scripteables", pero con distintas ideas:

Si se les ocurre una forma distinta de lograr esto, los invito a dejar un comentario con su código. Les recuerdo que pueden usar resaltado de sintaxis en los comentarios.

14 comentarios en este post

Feed de comentarios
  1. Avatar

    Gastón 12 enero. 2011 - 10:27

    Yo soy de los que usan sed. Tu solución puede tener problemas para renombrar, por ejemplo: “imgpl.pl”, quedaría “imgpng.pl”; también podría tener problemas si el nombre de archivo tiene espacios.
    Para evitar esos problemas:

    j=`echo $i | sed 's/pl$/png/'`
    mv "$i" "$j"

    Saludos!

  2. Avatar

    Arlequín 12 enero. 2011 - 15:40

    Es una bobada y no aporta nada, pero FS define el separador de campos (‘field separator’) y no separador de archivos 😉

  3. Avatar

    Ruben 12 enero. 2011 - 17:20

    Que tal usar el comando “rename”

    Usa la sintaxis de perl para especificar de que manera queres renombrar.

    por ej:

    rename 's/.pl$/.png/' *.pl

  4. Avatar

    duende 12 enero. 2011 - 20:34

    También se puede hacer usando la expansión de parámetros de bash :

    ls *.pl | while  read f; do mv $f ${f/.pl/.png}; done

  5. Avatar

    dr. Q 14 enero. 2011 - 19:43

    La linea donde entubas awk a awk te acaba de conceder el premio al uso inútil de awk, que es parecido a este uso inútil de grep.

    Tampoco hacía falta ls -l, basta con ls

    Resumiendo:

    ls | awk -F"." '/pl$/{ print "mv "" $1 ".pl" "" $1 ".png"" }' | bash

    • Avatar

      Fernando 14 enero. 2011 - 21:43

      Tenés razón, no era necesario entubar awk a awk, es un error que cometo bastante seguido. Gracias por la corrección, de ahora en más voy a tenerlo en cuenta para hacer scripts más simples.

      ¡Saludos!

  6. Avatar

    dklight 18 enero. 2011 - 17:01

    También podrías usar basename en lugar de sed, que te da el nombre del archivo sin extensión:

    #!/bin/sh
     
    for i in *.pl; do
        j=`basename $i .pl`.png
        mv $i $j
    done

    Otra de las 101 formas de renombrar archivos

    • Avatar

      Fernando 18 enero. 2011 - 20:55

      ¿No sería así?

      j=`basename $i`.png

      Lo de las 101 formas, podría ser una buena idea para hacer una recopilación de los scripts que he ido publicando y discutiendo en el blog…

      • Avatar

        fitorec 14 febrero. 2011 - 10:06

        Interesante la implementación con basename me gusto!, la verdad es que esta bien la implementación con el .ctp al final la documentación que la forma del comando es:

        basename nombre [sufijo]

        El problema es que elimina también directorios(yo no pude hacer que no los elimine) y si por ejemplo quisieras algo recursivo como:

        for i in $(find . -iregex ".*.pl$); do
            j=`basename $i .pl`.png
            mv $i $j
        done

        Esto al eliminar el directorio pondría todos los archivos con extensión .pl en el directorio actual con extensión .png, lo cual no es un comportamiento deseado ya que podríamos encimar archivos y perderlos.

        Otro detalle es que no podemos jugar al vuelo con el nombre del nuevo archivo.

        Para eso se tiene el buen sed ‘:¬), p.e.

        for i in $(find . -iregex ".*.pl$); do
            j=`echo $i | sed -r "s/([_a-z0-9]+)_org.pl$/1_dst.png/i"`
            mv $i $j
        done

        donde con \1 hacemos referencia a la cadena cachada con la expresión regular [_a-z0-9]+, podemos hacer múltiples referencias \2 \3 y por eso(y por otras cosas)el sed para mi es como mi Duvalín ‘:¬),

Dejar un comentario

Toasty!