Bash regex para cambiar el nombre del conjunto de archivos

Bash regex para cambiar el nombre del conjunto de archivos

Tengo que cambiar el nombre de un conjunto de archivos usando el renamecomando (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 .txtextensió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

$datahoratienefecha 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 prefixparte, y el último elemento es other.txtparte 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 arrayvariante solo escribe una vez en el disco.

¿Qué opinas de esas dos soluciones?...

Gracias de nuevo.

Respuesta1

A menos que renamepueda 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 DATETIMEvariable 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 renamevarios comandos, simplemente concatenelos:

rename 's/_/-/g;s/^prefix-(.*)-([^-]*).txt$/$2_$1_'$DATETIME'/' *txt

información relacionada