
Tengo que cambiar el nombre de un conjunto de archivos usando el rename
comando (con una expresión regular). Después de algunos intentos, no puedo encontrar una expresión que obtenga el resultado esperado.
Tengo un patrón de archivo como ese:
prefijo_algún_nombre_otro.txt
Todos los archivos comienzan con la prefix_
cadena " " y terminan con " _other.txt
", y elalgún_nombreparte puede constar de varias palabras (alfanuméricas) separadas por guiones bajos. Entonces es posible tener:
prefix_one_name_other.txt
prefix_this_is_my_name_1_this1_other.txt
Necesito cambiar el nombre de los archivos como estos:
otro_un-nombre_fecha y hora otro_este-es-mi-nombre-1-este1_fecha y hora
En otras palabras:
- Es necesario eliminar "
prefix
" (dejando el guión bajo) - "
other
" el token va al principio del nombre del archivo - Enalgún_nombre, convierte el guión bajo (_) en guión (-)
- El guión bajo al final del nombre del archivo (después dealgún_nombre) debe permanecer
- Es necesario eliminar la
.txt
extensión, reemplazada porfecha y hora.
Lo que he probado:
rename 's/fw_([a-z]+)_(\d)_(\w+\d)_(\w+)\.txt/$4_$1-$2-$3_'$datahora'/' *.txt
$datahora
tienefecha y horavalor (probado). Esto funciona como se esperaba con
prefix_name_1_gnt1_other.txt
pero no con
prefix_other_name_2_gnt2_other.txt
¿Qué hice mal? ¿De qué otra manera podría lograr eso?
Me he decidido porque por ahora no puedo encontrar una expresión regular que funcione para todos los nombres de archivos que tengo. Sé que el primer elemento de la cadena es siempre la prefix
parte, y el último elemento es other.txt
parte de la cadena. Entonces es posible dividir una cadena en una matriz y obtener los elementos que necesito para crear el nuevo nombre. De hecho, algo así.
datahora="20140718-080000"
arrfiles=( *.txt )
for curfile in ${arrfiles[*]}
do
arrparts=( ${curfile//_/ } )
numitems=${#arrparts[*]}
newname=""
for (( c=1; c<numitems-1; c++ ))
do
newname+="${arrparts[c]}-"
done
newname=${newname%-}
arrparts[numitems-1]=${arrparts[numitems-1]/.txt/}
newname="${arrparts[numitems-1]}_${newname}_$datahora"
echo "$curfile pasa a $newname"
mv ${curfile} ${newname}
done
Después de hacerlo de esta manera, probé de nuevo la sugerencia de @peterph y finalmente terminé con algunas combinaciones de expresiones regulares para cambiar el nombre. Algunos piensan así:
rename 's/_/-/g' *.txt
rename 's/^fw-(.*)-([^-]*)(\.txt)/$2.$1$3/' *.txt
rename 's/(\w+)\.(.*)(\.txt)/$1_$2_'$datahora'/' *.txt
No estoy seguro de cuál es el mejor enfoque. En mi opinión, la variante regex parece más elegante, pero necesito tres operaciones de cambio de nombre (acceder tres veces al disco) para hacer el trabajo, mientras que la array
variante solo escribe una vez en el disco.
¿Qué opinas de esas dos soluciones?...
Gracias de nuevo.
Respuesta1
A menos que rename
pueda aceptar múltiples comandos de sustituciónyla raíz del nombre del archivo ( some_name
) puede contener más de un guión bajo; debe hacerlo en dos pasos: a) reemplazar los guiones bajos con guiones y b) (re)mover fragmentos en los nombres de los archivos.
Las expresiones regulares que buscas pueden ser por ejemplo:
rename 's/_/-/g' *.txt
rename 's/^prefix-(.*)-([^-]*).txt$/$2_$1_'$DATETIME'/' *txt
El primero hace el guión bajo para las traducciones de guiones, mientras que el segundo intercambia la raíz y el sufijo y agrega el contenido de la DATETIME
variable de entorno a los nombres. Y omite el prefijo y la extensión, por supuesto.
La [^-]*
parte coincide con cualquier cadena que no contenga un guión. En caso de que el sufijo sea siempre el mismo, puede colocarlo allí palabra por palabra como es el caso con el prefijo (y viceversa; si el prefijo puede variar, utilícelo ^[^-]*-
para que coincida con cualquier cadena que no contenga un guión ubicado entre el principio del archivo). nombre y (por tanto) el primer guión).
Si admite rename
varios comandos, simplemente concatenelos:
rename 's/_/-/g;s/^prefix-(.*)-([^-]*).txt$/$2_$1_'$DATETIME'/' *txt