Cambiar el nombre de conjuntos de archivos con múltiples extensiones (incluidas las compuestas) en Shell

Cambiar el nombre de conjuntos de archivos con múltiples extensiones (incluidas las compuestas) en Shell

Tengo una lista de conjuntos de archivos con muchas extensiones, pero nombres únicos.

filename-1.foo.001
...
filename-1.foo.020
filename-1.foo.baz
filename-1.foo.bar1-2.baz
...
filename-1.foo.bar7-8.baz

another_filename.foo.001
another_filename.foo.002
...
another_filename.foo.009
another_filename.foo.baz
another_filename.foo.bar1-2.baz
another_filename.foo.bar3-4.baz
another_filename.foo.bar4-5.baz
another_filename.foo.bar7-8.baz

yet.a.different.file.name.foo.001
yet.a.different.file.name.foo.002
...
yet.a.different.file.name.foo.287
yet.a.different.file.name.foo.baz
yet.a.different.file.name.foo.bar1-2.baz
yet.a.different.file.name.foo.bar3-4.baz
yet.a.different.file.name.foo.bar4-5.baz
yet.a.different.file.name.foo.bar7-8.baz

moreFileNaming.foo.001
...
moreFileNaming.foo.009
moreFileNaming.foo.baz
moreFileNaming.foo.bar1-2.baz
moreFileNaming.foo.bar3-4.baz
moreFileNaming.foo.bar4-5.baz
moreFileNaming.foo.bar7-8.baz

Me gustaría cambiarles el nombre usando la salida de openssl rand -hex 8para obtener un nombre de archivo aleatorio para cada conjunto como este:

9874f7187c914ea9.foo.001
...
9874f7187c914ea9.foo.020
9874f7187c914ea9.foo.baz
9874f7187c914ea9.foo.bar1-2.baz
...
9874f7187c914ea9.foo.bar7-8.baz

2f54a0b6528e3927.foo.001
2f54a0b6528e3927.foo.002
...
2f54a0b6528e3927.foo.009
2f54a0b6528e3927.foo.baz
2f54a0b6528e3927.foo.bar1-2.baz
2f54a0b6528e3927.foo.bar3-4.baz
2f54a0b6528e3927.foo.bar4-5.baz
2f54a0b6528e3927.foo.bar7-8.baz

71ad0aa90148b2f5.foo.001
71ad0aa90148b2f5.foo.002
...
71ad0aa90148b2f5.foo.287
71ad0aa90148b2f5.foo.baz
71ad0aa90148b2f5.foo.bar1-2.baz
71ad0aa90148b2f5.foo.bar3-4.baz
71ad0aa90148b2f5.foo.bar4-5.baz
71ad0aa90148b2f5.foo.bar7-8.baz

3721323156e921b5.foo.001
...
3721323156e921b5.foo.009
3721323156e921b5.foo.baz
3721323156e921b5.foo.bar1-2.baz
3721323156e921b5.foo.bar3-4.baz
3721323156e921b5.foo.bar4-5.baz
3721323156e921b5.foo.bar7-8.baz

Lo he intentado for name (*.(<->|baz|bar<->.baz) result=$(openssl rand -hex 16) && mv $name $result(es posible que esto no funcione como lo fue hace varias iteraciones) pero cuando funcionó, me diocada archivoun nombre aleatorio, solo quiero que cada conjunto permanezca con el mismo nombre, solo aleatorio y con el mismo tamaño. Sha1sum o cualquier otro método también estaría bien.

¿Cómo logro esto? Especialmente para los archivos..foo.bar-*.baz ?

Si dejamos caer foo

3721323156e921b5.001
...
3721323156e921b5.009
3721323156e921b5.baz
3721323156e921b5.bar1-2.baz
3721323156e921b5.bar3-4.baz
3721323156e921b5.bar4-5.baz
3721323156e921b5.bar7-8.baz

También estaría bien. Algunas preguntas más:

  1. ¿Cómo apunto desde el principio del archivo hasta .foo?
  2. ¿Cómo hago un bucle con la variable creada, por ejemplo, result=$(openssl rand -hex 8)para usarla en los cambios de nombre, y solo cuando se completa un conjunto, para asignarla nuevamente y recorrerla hasta que se complete el siguiente conjunto, etc.?

¡Gracias!

Respuesta1

Este problema tiene varias partes:

  1. Descomponga cada nombre de archivo en una parte base y extensiones.
  2. Aplique una transformación consistente a la parte base de cada nombre.
  3. Cambie el nombre de los archivos según la transformación elegida de la pieza base, conservando las extensiones.

1. Descomponer nombres de archivos

No queda completamente claro en los nombres de su ejemplo qué considera que es la parte base de un nombre de archivo. El separador es evidentemente un punto, pero en un ejemplo como ¿ yet.a.different.file.name.foo.bar1-2.bazqué punto? Mencionas un intento de usar *.(<->|baz|bar<->.baz), que no trataría fooo bar1-2como una extensión. Un ajuste que los permite como extensión es .(foo|<->|baz|bar<->(|-<->).baz). Luego puede dividir el nombre de un archivo $fde la siguiente manera:

setopt extended_glob
base=${f%%(.(<->|baz|bar<->(|-<->).baz))#}; extensions=${f#$base}

Alternativamente, si es aceptable definir la base como todo hasta y excluyendo el primero .foo., la descomposición es más simple:

base=${f%*.foo.*}; extensions=${f#$base}

2. Aplicar una transformación consistente

Si desea aplicar una transformación determinista, puede volver a calcular cada vez. Por ejemplo, puede obtener un resultado pseudoaleatorio tomando una MAC del nombre con una clave secreta, utilizando la misma clave secreta cada vez.

secret=$(openssl rand -hex 32)
for … # Loop over the files as per (3.), set $base and $extensions as per (1.)
  new_base=${"$(openssl dgst -sha256 -hmac $secret <<<$base)"[-16,-1]}

(Nota: la clave secreta será visible para otros usuarios si ejecutan psmientras opensslse está ejecutando. Supongo que esto no es un problema en su caso, pero los futuros lectores deben tener cuidado con esto).

Si desea aplicar una transformación aleatoria, debe recordar a qué se asigna cada base. Hay dos maneras de hacerlo:

  • Puede agrupar archivos por su base y luego procesar una base a la vez.
  • Puede procesar los archivos uno por uno, pero recuerde a qué se asigna cada base y solo genere un nuevo mapeo si la base aún no se ha visto.

El segundo método es más fácil y el primero no tiene ninguna ventaja particular, así que sólo mostraré el segundo método.

construir unmatriz asociativamapeo de bases a nuevas bases.

typeset -A mapping
mapping=()
for … # Loop over the files as per (3.), set $base and $extensions as per (1.)
  if ((!$+mapping[$base])); then
    mapping[$base]=$(openssl rand -hex 8)
  fi
  new_base=$mapping[$base]

3. Cambiar el nombre de los archivos

Zsh viene con una herramienta muy útil para cambiar el nombre de archivos:zmv. La transformación que desea realizar es lo suficientemente compleja como para que zmv no la haga trivial: tanto la descomposición del nombre del archivo como la transformación requieren trabajo adicional. Zmv tiene algunas ventajas menores incluso en su caso. En particular, zmv producirá un error si hay un conflicto (es extremadamente improbable debido a factores aleatorios a menos que utilice longitudes más cortas). Sin embargo, debido a la dificultad en la transformación del nombre, usar zmv es complicado y un bucle simple es más fácil de escribir.

Aquí hay un fragmento completo con nombres aleatorios.

setopt extended_glob
typeset -A mapping
mapping=()
for f in *.(foo|<->|baz|bar<->(|-<->).baz); do
  base=${f%%(.(foo|<->|baz|bar<->(|-<->).baz))#}; extensions=${f#$base}
  if ((!$+mapping[$base])); then
    mapping[$base]=$(openssl rand -hex 8)
  fi
  new_base=$mapping[$base]
  mv -i -- $f $new_base$extensions
done

Aquí hay un fragmento completo que utiliza nombres deterministas para un valor determinado de $secret.

setopt extended_glob
secret=$(openssl rand -hex 32)
for f in *.(foo|<->|baz|bar<->(|-<->).baz); do
  base=${f%%(.(foo|<->|baz|bar<->(|-<->).baz))#}; extensions=${f#$base}
  new_base=${"$(openssl dgst -sha256 -hmac $secret <<<$base)"[-16,-1]}
  mv -i -- $f $new_base$extensions
done

Y aquí hay una sola línea que se usa zmvpara el caso determinista, usando la primera .foo.para marcar el final de la base. La -wbandera ayuda con la avería.

autoload zmv
secret=$(openssl rand -hex 32)
zmv -w '*.foo.*' '${"$(openssl dgst -sha256 -hmac $secret <<<$1)"[-16,-1]}.foo.$2'

Usar zmv en el caso aleatorio es más complicado porque necesitamos preservar la información de un paso de transformación al siguiente. Podemos simplemente empaquetar algo de código en una sustitución de comando zmv … '$(…; if …; then mapping[$base]=…; …)'porque la asignación estaría mappingdentro del subshell de sustitución de comando y, por lo tanto, solo tendría un efecto dentro del subshell. Sin embargo, podemos utilizar una asignación de parámetros condicional.${name=word}, para configurar mapping[$base]solo si está desarmado.

typeset -A mapping; mapping=()
zmv -w '*.foo.*' '${mapping[${1}]=$(openssl rand -hex 16)}.foo.$2'

Usar zmv con una descomposición que no aprovecha .foo, como el ejemplo más complejo en (1.) anterior, da como resultado un código mucho más complejo. Solo por el ejemplo, aquí hay una invocación zmv para el caso determinista, usándola basecomo variable temporal para almacenar el nombre base. Se utiliza ${name::=word}para asignar a una variable durante la expansión de parámetros y ${…}[0]para suprimir esa parte de la expansión ( [0]toma la subcadena del carácter 0, que no existe ya que zsh comienza a numerar elementos de matriz y caracteres de cadena en 1; algo así [2,1]también funcionaría ).

zmv  '*.(<->|baz|bar<->.baz)' '${${base::=${f%%(.(<->|baz|bar<->(|-<->).baz))#}}[0]}${"$(openssl dgst -sha256 -hmac $secret <<<$base)"[-16,-1]}.${f#$base}'

Respuesta2

Podrías hacer algo como:

autoload -Uz zmv # best in ~/.zshrc
typeset -A rand
zmv '(*).foo(.*)' '${rand[$1]=$(openssl rand -hex 8)}$2'

o '(*)(.foo.*)'no caer .foo.

Para probar primero, agregue la -nopción (ejecución en seco) a zmv.

zmves una herramienta de cambio de nombre por lotes implementada como una función autocargable.

El primer argumento es un patrón global extendido y el segundo argumento es una cadena que sufre expansiones de palabras que determina cómo se eliminan los archivos con $1, $2... refiriéndose a los s correspondientes (...)en el patrón.

${rand[$1]=$(cmd)}arriba establece el miembro de la matriz asociativa para elllavesiendo lo que queda del extremo derecho .foo.de la salida de cmdif no estaba configurado previamente, lo que garantiza que siempre obtenga el mismo valor de un determinadollave.

información relacionada