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 8
para 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:
- ¿Cómo apunto desde el principio del archivo hasta .foo?
- ¿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:
- Descomponga cada nombre de archivo en una parte base y extensiones.
- Aplique una transformación consistente a la parte base de cada nombre.
- 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.baz
qué punto? Mencionas un intento de usar *.(<->|baz|bar<->.baz)
, que no trataría foo
o bar1-2
como una extensión. Un ajuste que los permite como extensión es .(foo|<->|baz|bar<->(|-<->).baz)
. Luego puede dividir el nombre de un archivo $f
de 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 ps
mientras openssl
se 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 zmv
para el caso determinista, usando la primera .foo.
para marcar el final de la base. La -w
bandera 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 mapping
dentro 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 base
como 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 -n
opción (ejecución en seco) a zmv
.
zmv
es 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 cmd
if no estaba configurado previamente, lo que garantiza que siempre obtenga el mismo valor de un determinadollave.