Renombrar varios archivos desde la línea de comando
Publicado el 12 de enero de 2011Hace 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).
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:
- AWK: Pasar texto a minúsculas/mayúsculas
- Matando procesos con expresiones regulares
- Genocidio de procesos con expresiones regulares
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.
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:
Saludos!
Fernando 12 enero. 2011 - 12:38
Es cierto el problema que señalas. Incluí el $ en la expresión regular del awk, pero no del sed. Gracias por la corrección!
Saludos
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 😉
Fernando 12 enero. 2011 - 18:42
Ups, me confundí con “file separator” 😛
Gracias por la corrección, ya quedó arreglado 🙂
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:
Fernando 12 enero. 2011 - 18:44
Bueno sí, rename fue una de las opciones, pero no venía instalado por defecto.
De todas formas es bueno tenerlo en cuenta.
Gracias por comentar!
duende 12 enero. 2011 - 20:34
También se puede hacer usando la expansión de parámetros de bash :
Fernando 14 enero. 2011 - 21:47
Gracias por aportar otra solución con Bash, bastante simple también.
dr. Q 14 enero. 2011 - 19:43
La linea donde entubas
awk
aawk
te acaba de conceder el premio al uso inútil deawk
, que es parecido a este uso inútil degrep
.Tampoco hacía falta
ls -l
, basta conls
Resumiendo:
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!
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:
Otra de las 101 formas de renombrar archivos
Fernando 18 enero. 2011 - 20:55
¿No sería así?
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…
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:
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.
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 ‘:¬),
Aran 18 octubre. 2011 - 02:01