Pregunta

  

Especificación Problema:

     

Dado un directorio, quiero recorrer el directorio y su no-ocultos subdirectorios, España   y añadir un hash de hidromasaje en la no oculta   nombres de archivo.
  Si el guión se vuelve a ejecutar sería reemplazaría un viejo hash con una nueva.

     
    

<filename>.<extension> ==> <filename>.<a-whirlpool-hash>.<extension>

         

<filename>.<old-hash>.<extension> ==> <filename>.<new-hash>.<extension>

  


  

Pregunta:

     

a) ¿Cómo hacer esto?

     

b) De los todos los métodos disponibles para usted, lo que hace que su método más adecuado?


  

Veredicto:

     

Gracias a todos, he elegido la respuesta de SeigeX por su velocidad y portabilidad.
  Es emprically más rápido que las otras variantes de bash, España   y funcionó sin alteración en mi máquina Mac OS X.

¿Fue útil?

Solución

Actualización de arreglar:
1. Los nombres de archivo con '[' o ']' en su nombre (en realidad, cualquier personaje ahora. Ver comentario)
2. Manipulación de suma md5 hash cuando un archivo con una barra invertida o nueva línea en su nombre
3. Functionized algo de hash de comprobación para la modularidad
4. refactorizado lógica de hash de comprobación para eliminar los dobles negativos

#!/bin/bash
if (($# != 1)) || ! [[ -d "$1" ]]; then
    echo "Usage: $0 /path/to/directory"
    exit 1
fi

is_hash() {
 md5=${1##*.} # strip prefix
 [[ "$md5" == *[^[:xdigit:]]* || ${#md5} -lt 32 ]] && echo "$1" || echo "${1%.*}"
}

while IFS= read -r -d $'\0' file; do
    read hash junk < <(md5sum "$file")
    basename="${file##*/}"
    dirname="${file%/*}"
    pre_ext="${basename%.*}"
    ext="${basename:${#pre_ext}}"

    # File already hashed?
    pre_ext=$(is_hash "$pre_ext")
    ext=$(is_hash "$ext")

    mv "$file" "${dirname}/${pre_ext}.${hash}${ext}" 2> /dev/null

done < <(find "$1" -path "*/.*" -prune -o \( -type f -print0 \))

Este código tiene las siguientes ventajas sobre otras entradas hasta el momento

  • Es totalmente compatible con las versiones 2.0.2 y más allá del golpe
  • No hay llamadas innecesarias a otros binarios como sed o grep; utiliza la expansión de parámetro incorporado en lugar
  • Usos sustitución proceso de 'encontrar' en lugar de un tubo, no sub-cáscara se hace de esta manera
  • Toma el directorio para trabajar en como un argumento y realiza una comprobación de validez en ella
  • Utiliza $ () en lugar de `` notación para la sustitución de comandos, este último está en desuso
  • funciona con archivos con espacios
  • funciona con archivos con saltos de línea
  • funciona con archivos con múltiples extensiones
  • Trabaja con archivos sin extensión
  • no atraviesa directorios ocultos
  • ¿El no de omisión de archivos pre-hash, se volverá a calcular el hash de acuerdo con la especificación

El árbol de prueba

$ tree -a a
a
|-- .hidden_dir
|   `-- foo
|-- b
|   `-- c.d
|       |-- f
|       |-- g.5236b1ab46088005ed3554940390c8a7.ext
|       |-- h.d41d8cd98f00b204e9800998ecf8427e
|       |-- i.ext1.5236b1ab46088005ed3554940390c8a7.ext2
|       `-- j.ext1.ext2
|-- c.ext^Mnewline
|   |-- f
|   `-- g.with[or].ext
`-- f^Jnewline.ext

4 directories, 9 files 

Resultados

$ tree -a a
a
|-- .hidden_dir
|   `-- foo
|-- b
|   `-- c.d
|       |-- f.d41d8cd98f00b204e9800998ecf8427e
|       |-- g.d41d8cd98f00b204e9800998ecf8427e.ext
|       |-- h.d41d8cd98f00b204e9800998ecf8427e
|       |-- i.ext1.d41d8cd98f00b204e9800998ecf8427e.ext2
|       `-- j.ext1.d41d8cd98f00b204e9800998ecf8427e.ext2
|-- c.ext^Mnewline
|   |-- f.d41d8cd98f00b204e9800998ecf8427e
|   `-- g.with[or].d41d8cd98f00b204e9800998ecf8427e.ext
`-- f^Jnewline.d3b07384d113edec49eaa6238ad5ff00.ext

4 directories, 9 files

Otros consejos

#!/bin/bash
find -type f -print0 | while read -d $'\0' file
do
    md5sum=`md5sum "${file}" | sed -r 's/ .*//'`
    filename=`echo "${file}" | sed -r 's/\.[^./]*$//'`
    extension="${file:${#filename}}"
    filename=`echo "${filename}" | sed -r 's/\.md5sum-[^.]+//'`
    if [[ "${file}" != "${filename}.md5sum-${md5sum}${extension}" ]]; then
        echo "Handling file: ${file}"
        mv "${file}" "${filename}.md5sum-${md5sum}${extension}"
    fi
done
  • Probado en archivos que contienen espacios como 'a b'
  • Probado en archivos que contienen múltiples extensiones como 'A.B.C'
  • Probado con los directorios que contienen espacios y / o puntos.
  • Probado en archivos que contienen ninguna extensión dentro de los directorios que contienen los puntos, como 'a.b / c'
  • Actualización . Ahora actualiza hashes si los cambios de archivos

Los puntos clave:

  • El uso de hilo print0 a while read -d $'\0', para manejar correctamente los espacios en los nombres de archivo.
  • md5sum puede ser reemplazado con su función hash favorito. La sed elimina el primer espacio y todo después de que desde la salida del md5sum.
  • El nombre de archivo de base se extrajo usando una expresión regular que encuentra el último período que no está seguida por otra barra (de modo que los períodos en los nombres de directorio no se cuentan como parte de la extensión).
  • La extensión se encuentra mediante el uso de una subcadena con el inicio de índice como la longitud del nombre de archivo base.

La lógica de los requisitos es suficientemente complejo como para justificar el uso de Python en lugar de bash. Debe proporcionar una solución más fácil de leer, extensible y mantenible.

#!/usr/bin/env python
import hashlib, os

def ishash(h, size):
    """Whether `h` looks like hash's hex digest."""
    if len(h) == size: 
        try:
            int(h, 16) # whether h is a hex number
            return True
        except ValueError:
            return False

for root, dirs, files in os.walk("."):
    dirs[:] = [d for d in dirs if not d.startswith(".")] # skip hidden dirs
    for path in (os.path.join(root, f) for f in files if not f.startswith(".")):
        suffix = hash_ = "." + hashlib.md5(open(path).read()).hexdigest()
        hashsize = len(hash_) - 1
        # extract old hash from the name; add/replace the hash if needed
        barepath, ext = os.path.splitext(path) # ext may be empty
        if not ishash(ext[1:], hashsize):
            suffix += ext # add original extension
            barepath, oldhash = os.path.splitext(barepath) 
            if not ishash(oldhash[1:], hashsize):
               suffix = oldhash + suffix # preserve 2nd (not a hash) extension
        else: # ext looks like a hash
            oldhash = ext
        if hash_ != oldhash: # replace old hash by new one
           os.rename(path, barepath+suffix)

Aquí hay un árbol de directorios de prueba. Contiene:

  • Los archivos sin extensión dentro de los directorios con un punto en su nombre
  • nombre de archivo que ya tiene un hash en el mismo (prueba en idempotencia)
  • nombre de archivo con dos extensiones
  • salto de línea de nombres
$ tree a
a
|-- b
|   `-- c.d
|       |-- f
|       |-- f.ext1.ext2
|       `-- g.d41d8cd98f00b204e9800998ecf8427e
|-- c.ext^Mnewline
|   `-- f
`-- f^Jnewline.ext1

7 directories, 5 files

Resultados

$ tree a
a
|-- b
|   `-- c.d
|       |-- f.0bee89b07a248e27c83fc3d5951213c1
|       |-- f.ext1.614dd0e977becb4c6f7fa99e64549b12.ext2
|       `-- g.d41d8cd98f00b204e9800998ecf8427e
|-- c.ext^Mnewline
|   `-- f.0bee89b07a248e27c83fc3d5951213c1
`-- f^Jnewline.b6fe8bb902ca1b80aaa632b776d77f83.ext1

7 directories, 5 files

La solución funciona correctamente para todos los casos.


Whirlpool de hash no está en stdlib de Python, pero hay dos puros extensiones de Python y C que lo soportan, por ejemplo, python-mhash.

Para instalarlo:

$ sudo apt-get install python-mhash

Para usarlo:

import mhash

print mhash.MHASH(mhash.MHASH_WHIRLPOOL, "text to hash here").hexdigest()

Salida:     cbdca4520cc5c131fc3a86109dd23fee2d7ff7be56636d398180178378944a4f41480b938608ae98da7eccbf39a4c79b83a8590c4cb1bace5bc638fc92b3e653


Invocación whirlpooldeep en Python

from subprocess import PIPE, STDOUT, Popen

def getoutput(cmd):
    return Popen(cmd, stdout=PIPE, stderr=STDOUT).communicate()[0]

hash_ = getoutput(["whirlpooldeep", "-q", path]).rstrip()

git puede proporcionar con un apalancamiento de los problemas que necesitan realizar un seguimiento conjunto de archivos en función de sus valores hash.

Yo no estaba muy contento con mi primera respuesta, ya que como he dicho ahí, este problema parece que está mejor resuelto con el Perl. Usted ya se ha dicho en una edición de su pregunta que usted tiene Perl en la máquina OS X que desea ejecutar esta opción, así que decidí probarlo.

Es difícil conseguir todo derecho en bash, es decir, evitando cualquier problema citando con nombres extraños, y de comportarse bien con los nombres de archivo esquina de los casos.

Así que aquí está en Perl, una solución completa a su problema. Se extiende sobre todos los archivos / directorios que aparecen en su línea de comandos.


#!/usr/bin/perl -w
# whirlpool-rename.pl
# 2009 Peter Cordes <peter@cordes.ca>.  Share and Enjoy!

use Fcntl;      # for O_BINARY
use File::Find;
use Digest::Whirlpool;

# find callback, called once per directory entry
# $_ is the base name of the file, and we are chdired to that directory.
sub whirlpool_rename {
    print "find: $_\n";
#    my @components = split /\.(?:[[:xdigit:]]{128})?/; # remove .hash while we're at it
    my @components = split /\.(?!\.|$)/, $_, -1; # -1 to not leave out trailing dots

    if (!$components[0] && $_ ne ".") { # hidden file/directory
        $File::Find::prune = 1;
        return;
    }

    # don't follow symlinks or process non-regular-files
    return if (-l $_ || ! -f _);

    my $digest;
    eval {
        sysopen(my $fh, $_, O_RDONLY | O_BINARY) or die "$!";
        $digest = Digest->new( 'Whirlpool' )->addfile($fh);
    };
    if ($@) {  # exception-catching structure from whirlpoolsum, distributed with Digest::Whirlpool.
        warn "whirlpool: couldn't hash $_: $!\n";
        return;
    }

    # strip old hashes from the name.  not done during split only in the interests of readability
    @components = grep { !/^[[:xdigit:]]{128}$/ }  @components;
    if ($#components == 0) {
        push @components, $digest->hexdigest;
    } else {
        my $ext = pop @components;
        push @components, $digest->hexdigest, $ext;
    }

    my $newname = join('.', @components);
    return if $_ eq $newname;
    print "rename  $_ ->  $newname\n";
    if (-e $newname) {
        warn "whirlpool: clobbering $newname\n";
        # maybe unlink $_ and return if $_ is older than $newname?
        # But you'd better check that $newname has the right contents then...
    }
    # This could be link instead of rename, but then you'd have to handle directories, and you can't make hardlinks across filesystems
    rename $_, $newname or warn "whirlpool: couldn't rename $_ -> $newname:  $!\n";
}


#main
$ARGV[0] = "." if !@ARGV;  # default to current directory
find({wanted => \&whirlpool_rename, no_chdir => 0}, @ARGV );

Ventajas: - en realidad utiliza hidromasaje, para que pueda utilizar este programa exacto directamente. (Después de instalar libperl-digerir-hidromasaje). Fácil de cambiar a cualquier compendio función que desea, porque en vez de diferentes programas con diferentes formatos de salida, usted tiene la interfaz común Perl Digest.

  • implementa todos los demás requisitos: ignorar los archivos ocultos (y archivos en directorios ocultos)

  • .
  • capaz de manejar cualquier nombre de archivo posible sin error o problema de seguridad. (Varias personas llegaron este derecho en sus scripts de shell).

  • sigue las mejores prácticas para recorrer un árbol de directorios, por chdiring abajo en cada directorio (como mi respuesta anterior, con el hallazgo -execdir). Esto evita problemas con PATH_MAX, y con directorios que va a cambiar el nombre mientras se está ejecutando.

  • manipulación inteligente de nombres de archivo que terminan con. foo..txt ... -> foo..hash.txt ...

  • Maneja los nombres de archivo que contienen los viejos valores hash ya sin cambiar el nombre de ellos y luego cambiar el nombre de nuevo. (Se quita cualquier secuencia de 128 dígitos hexadecimales que está rodeado por el "" caracteres.) En el caso de que todo-correcta, no hay actividad de escritura en disco ocurre, simplemente lee de cada archivo. Su solución actual se mv dos veces en el caso ya de por el nombre correcto-, causando metadatos directorio escribe. Y siendo más lento, porque eso es dos procesos que tienen que ser execced.

  • eficiente. No hay programas son tenedor / Execed, mientras que la mayoría de las soluciones que realmente funcionaría bien llegó a tener algo de sed por archivo. Digest :: hidromasaje se implementa con una librería compartida compilado de forma nativa-, así que no es lento pura-perl. Esto debería ser más rápido que ejecutar un programa en cada fichero, esp. para archivos pequeños.?

  • Perl soporta cadenas UTF-8, por lo que los nombres de archivo con caracteres no ASCII no debería ser un problema. (No estoy seguro si cualquiera de las secuencias de múltiples bytes en UTF-8 podría incluir el byte que significa ASCII '' por su cuenta. Si eso es posible, entonces necesita UTF-8 manejo de cadenas conscientes. Sed no sabe UTF-8 . glob expresiones de Bash puede.)

  • fácilmente extensible. Cuando vas a poner esto en un programa real, y desea manejar más casos de esquina, puede hacerlo con bastante facilidad. p.ej. decidir qué hacer cuando se quiere cambiar el nombre de un archivo, pero el nombre del archivo hash llamado ya existe.

  • buenos informes de errores. La mayoría de los scripts de shell tienen esto, sin embargo, al pasar a lo largo de los errores de los progs que corren.

find . -type f -print | while read file
do
    hash=`$hashcommand "$file"`
    filename=${file%.*}
    extension=${file##*.}
    mv $file "$filename.$hash.$extension"
done

Es posible que desee almacenar los resultados en un archivo, como en

find . -type f -exec md5sum {} \; > MD5SUMS

Si realmente quiere un archivo por cada almohadilla:

find . -type f | while read f; do g=`md5sum $f` > $f.md5; done

o incluso

find . -type f | while read f; do g=`md5sum $f | awk '{print $1}'`; echo "$g $f"> $f-$g.md5; done

Esta es mi opinión sobre ella, en bash. Características: omite los archivos que no son fijos; correctamente se ocupa de archivos con caracteres extraños (es decir, espacios) en sus nombres; se ocupa de los nombres de archivo sin extensión; omite los archivos hash ya, de modo que se puede ejecutar en varias ocasiones (aunque si los archivos se modifican entre las corridas, se añade la nueva almohadilla en lugar de sustituir el viejo). Lo escribí usando MD5 -q como la función hash; usted debe ser capaz de reemplazar esto con cualquier otra cosa, con tal de que sólo produce la salida del hash, no algo así como el nombre de archivo => hash.

find -x . -type f -print0 | while IFS="" read -r -d $'\000' file; do
    hash="$(md5 -q "$file")" # replace with your favorite hash function
    [[ "$file" == *."$hash" ]] && continue # skip files that already end in their hash
    dirname="$(dirname "$file")"
    basename="$(basename "$file")"
    base="${basename%.*}"
    [[ "$base" == *."$hash" ]] && continue # skip files that already end in hash + extension
    if [[ "$basename" == "$base" ]]; then
            extension=""
    else
            extension=".${basename##*.}"
    fi
    mv "$file" "$dirname/$base.$hash$extension"
done

En SH o golpe, dos versiones. Uno se limita a los archivos con extensiones ...

hash () {
  #openssl md5 t.sh | sed -e 's/.* //'
  whirlpool "$f"
}

find . -type f -a -name '*.*' | while read f; do
  # remove the echo to run this for real
  echo mv "$f" "${f%.*}.whirlpool-`hash "$f"`.${f##*.}"
done

Pruebas ...

...
mv ./bash-4.0/signames.h ./bash-4.0/signames.whirlpool-d71b117a822394a5b273ea6c0e3f4dc045b1098326d39864564f1046ab7bd9296d5533894626288265a1f70638ee3ecce1f6a22739b389ff7cb1fa48c76fa166.h
...

Y esta versión más compleja procesa todos los archivos planos, con o sin extensiones, con o sin espacios y caracteres impares, etc, etc ...

hash () {
  #openssl md5 t.sh | sed -e 's/.* //'
  whirlpool "$f"
}

find . -type f | while read f; do
  name=${f##*/}
  case "$name" in
    *.*) extension=".${name##*.}" ;;
    *)   extension=   ;;
  esac
  # remove the echo to run this for real
  echo mv "$f" "${f%/*}/${name%.*}.whirlpool-`hash "$f"`$extension"
done

hidromasaje no es un hash muy común. Es probable que tenga que instalar un programa para calcularla. p.ej. Debian / Ubuntu incluye un paquete de "torbellino". El programa imprime el hash de un archivo por sí mismo. apt-cache search hidromasaje muestra que algunos otros paquetes apoyan, incluyendo el md5deep interesante.

Algunos de los anwsers anteriores se producirá un error en los nombres de archivo con espacios en ellos. Si este es el caso, pero los archivos no tienen ningún saltos de línea en el nombre del archivo, entonces se puede utilizar con seguridad \ n como delimitador.


oldifs="$IFS"
IFS="
"
for i in $(find -type f); do echo "$i";done
#output
# ./base
# ./base2
# ./normal.ext
# ./trick.e "xt
# ./foo bar.dir ext/trick' (name "- }$foo.ext{}.ext2
IFS="$oldifs"

tratar sin establecer IFS para ver por qué es importante.

Me iba a intentar algo con IFS = ""; encontrar -print0 | mientras lee -a matriz, para dividir en "" personajes, pero normalmente nunca usan las variables de matriz. No hay manera fácil que veo en la página del manual para insertar el hash como la penúltima índice de matriz, y empuje hacia abajo el último elemento (la extensión del archivo, si tuviera uno.) En cualquier momento las variables de matriz fiesta mirada interesante, sé es el momento de hacer lo que estoy haciendo en perl en su lugar! Ver las trampas para el uso de lectura: http://tldp.org/LDP/abs/html/gotchas.html# BADREAD0

decidí usar otra técnica que me gusta: encontrar -exec sh -c. Es el más seguro, ya que no está al analizar los nombres de archivo.

Esto debería hacer el truco:


find -regextype posix-extended -type f -not -regex '.*\.[a-fA-F0-9]{128}.*'  \
-execdir bash -c 'for i in "${@#./}";do 
 hash=$(whirlpool "$i");
 ext=".${i##*.}"; base="${i%.*}";
 [ "$base" = "$i" ] && ext="";
 newname="$base.$hash$ext";
 echo "ext:$ext  $i -> $newname";
 false mv --no-clobber "$i" "$newname";done' \
dummy {} +
# take out the "false" before the mv, and optionally take out the echo.
# false ignores its arguments, so it's there so you can
# run this to see what will happen without actually renaming your files.
ficticio

-execdir fiesta -c 'cmd' {} + arg tiene el maniquí allí porque el primer argumento después de la orden se convierte en $ 0 en los parámetros de posición de la concha, que no forma parte de "$ @" que para los bucles más. Puedo utilizar execdir en lugar de exec, así que no tengo que lidiar con los nombres de directorio (o la posibilidad de superar PATH_MAX de directorios anidados con nombres largos, cuando los nombres de archivo reales son lo suficientemente corta todo.)

-no -regex impide que esto se aplica dos veces para el mismo archivo. A pesar de hidromasaje es un tiempo extremadamente largo de hash, y mv dice Nombre de archivo demasiado largo si lo ejecuto dos veces sin que el registro. (En un sistema de ficheros XFS.)

Los archivos sin extensión obtener basename.hash. Tenía que comprobar especialmente para evitar añadiendo un final., O conseguir el nombre base como la extensión. ${@#./} pela el líder ./ que encontrar pone delante de cada nombre de fichero, por lo que no hay "" en toda la cadena de archivos sin extensión.

mv --no-clobber puede ser una extensión de GNU. Si usted no tiene mv GNU, hacer algo más si se quiere evitar la eliminación de archivos existentes (por ejemplo, corre por esta vez, algunos de los mismo archivo se añaden al directorio con sus nombres antiguos;. Ejecutar de nuevo) otoh, si desea que el comportamiento, que acaba de tomar a cabo.

Mi solución debería funcionar incluso cuando los nombres de archivo contienen una nueva línea (que puede, usted sabe!), O de cualquier otro carácter posible. Sería más rápido y más fácil en Perl, pero se pidió para la cáscara.

La solución de Wallenborn para hacer un archivo con todas las sumas de comprobación (en lugar de cambiar el nombre del original) es bastante bueno, pero ineficiente. No ejecute md5sum una vez por archivo, ejecutarlo en tantos archivos a la vez como quepan en su línea de comandos:

encontrar dir type f -print0 | xargs -0 md5sum> dir.md5 o con GNU encontrar, xargs está incorporada (tenga en cuenta el lugar de + ';')  dir encontrar md5sum -exec f de tipo {} +> dir.md5

Si sólo utiliza encontrar -print | xargs -d '\ n', se le cagado por nombres de archivo con comillas en ellos, así que ten cuidado. Si usted no sabe qué archivos que puede encontrar algún día este script en, siempre trate de usar print0 o -exec. Esto es especialmente. cierto si los nombres de archivo son suministrados por los usuarios no son de confianza (es decir, puede ser un vector de ataque en su servidor.)

En respuesta a su pregunta actualización:

  

Si alguien puede comentar sobre cómo puedo evitar mirar en directorios ocultos con mi escritura del golpe, sería muy apreciado.

Puede evitar directorios ocultos con hallazgo utilizando

find -name '.?*' -prune -o \( -type f -print0 \)

-name '.*' -prune podará "", y dejar sin hacer nada. : /

Me aún así, recomiendo mi versión de Perl, sin embargo. He actualizado que ... Es posible que aún tenga que instalar Digest :: hidromasaje de CPAN, sin embargo.

Hm, interesante problema.

Trate lo siguiente (la función mktest es sólo para las pruebas - TDD para bash:!)

Editar:

  • Se ha añadido soporte para los hashes de hidromasaje.
  • limpieza del código
  • mejor cita de nombres de archivo
  • cambiado matriz de sintaxis para la prueba parcialmente: ahora debería funcionar con la mayoría de los shells de Korn-como. Tenga en cuenta que pdksh no soporta: la expansión de parámetro basado en (o mejor dicho, que significa algo más)

Tenga en cuenta también que cuando se encuentra en modo de MD5-falla por nombres de archivo con los hashes de remolino, y posiblemente viceversa.

#!/usr/bin/env bash

#Tested with:
# GNU bash, version 4.0.28(1)-release (x86_64-pc-linux-gnu)
# ksh (AT&T Research) 93s+ 2008-01-31
# mksh @(#)MIRBSD KSH R39 2009/08/01 Debian 39.1-4
# Does not work with pdksh, dash

DEFAULT_SUM="md5"

#Takes a parameter, as root path
# as well as an optional parameter, the hash function to use (md5 or wp for whirlpool).
main()
{
  case $2 in
    "wp")
      export SUM="wp"
      ;;
    "md5")
      export SUM="md5"
      ;;
    *)
      export SUM=$DEFAULT_SUM
      ;;
  esac

  # For all visible files in all visible subfolders, move the file
  # to a name including the correct hash:
  find $1 -type f -not -regex '.*/\..*' -exec $0 hashmove '{}' \;
}

# Given a file named in $1 with full path, calculate it's hash.
# Output the filname, with the hash inserted before the extention
# (if any) -- or:  replace an existing hash with the new one,
# if a hash already exist.
hashname_md5()
{
  pathname="$1"
  full_hash=`md5sum "$pathname"`
  hash=${full_hash:0:32}
  filename=`basename "$pathname"`
  prefix=${filename%%.*}
  suffix=${filename#$prefix}

  #If the suffix starts with something that looks like an md5sum,
  #remove it:
  suffix=`echo $suffix|sed -r 's/\.[a-z0-9]{32}//'`

  echo "$prefix.$hash$suffix"
}

# Same as hashname_md5 -- but uses whirlpool hash.
hashname_wp()
{
  pathname="$1"
  hash=`whirlpool "$pathname"`
  filename=`basename "$pathname"`
  prefix=${filename%%.*}
  suffix=${filename#$prefix}

  #If the suffix starts with something that looks like an md5sum,
  #remove it:
  suffix=`echo $suffix|sed -r 's/\.[a-z0-9]{128}//'`

  echo "$prefix.$hash$suffix"
}


#Given a filepath $1, move/rename it to a name including the filehash.
# Try to replace an existing hash, an not move a file if no update is
# needed.
hashmove()
{
  pathname="$1"
  filename=`basename "$pathname"`
  path="${pathname%%/$filename}"

  case $SUM in
    "wp")
      hashname=`hashname_wp "$pathname"`
      ;;
    "md5")
      hashname=`hashname_md5 "$pathname"`
      ;;
    *)
      echo "Unknown hash requested"
      exit 1
      ;;
  esac

  if [[ "$filename" != "$hashname" ]]
  then
      echo "renaming: $pathname => $path/$hashname"
      mv "$pathname" "$path/$hashname"
  else
    echo "$pathname up to date"
  fi
}

# Create som testdata under /tmp
mktest()
{
  root_dir=$(tempfile)
  rm "$root_dir"
  mkdir "$root_dir"
  i=0
  test_files[$((i++))]='test'
  test_files[$((i++))]='testfile, no extention or spaces'

  test_files[$((i++))]='.hidden'
  test_files[$((i++))]='a hidden file'

  test_files[$((i++))]='test space'
  test_files[$((i++))]='testfile, no extention, spaces in name'

  test_files[$((i++))]='test.txt'
  test_files[$((i++))]='testfile, extention, no spaces in name'

  test_files[$((i++))]='test.ab8e460eac3599549cfaa23a848635aa.txt'
  test_files[$((i++))]='testfile, With (wrong) md5sum, no spaces in name'

  test_files[$((i++))]='test spaced.ab8e460eac3599549cfaa23a848635aa.txt'
  test_files[$((i++))]='testfile, With (wrong) md5sum, spaces in name'

  test_files[$((i++))]='test.8072ec03e95a26bb07d6e163c93593283fee032db7265a29e2430004eefda22ce096be3fa189e8988c6ad77a3154af76f582d7e84e3f319b798d369352a63c3d.txt'
  test_files[$((i++))]='testfile, With (wrong) whirlpoolhash, no spaces in name'

  test_files[$((i++))]='test spaced.8072ec03e95a26bb07d6e163c93593283fee032db7265a29e2430004eefda22ce096be3fa189e8988c6ad77a3154af76f582d7e84e3f319b798d369352a63c3d.txt']
  test_files[$((i++))]='testfile, With (wrong) whirlpoolhash, spaces in name'

  test_files[$((i++))]='test space.txt'
  test_files[$((i++))]='testfile, extention, spaces in name'

  test_files[$((i++))]='test   multi-space  .txt'
  test_files[$((i++))]='testfile, extention, multiple consequtive spaces in name'

  test_files[$((i++))]='test space.h'
  test_files[$((i++))]='testfile, short extention, spaces in name'

  test_files[$((i++))]='test space.reallylong'
  test_files[$((i++))]='testfile, long extention, spaces in name'

  test_files[$((i++))]='test space.reallyreallyreallylong.tst'
  test_files[$((i++))]='testfile, long extention, double extention,
                        might look like hash, spaces in name'

  test_files[$((i++))]='utf8test1 - æeiaæå.txt'
  test_files[$((i++))]='testfile, extention, utf8 characters, spaces in name'

  test_files[$((i++))]='utf8test1 - 漢字.txt'
  test_files[$((i++))]='testfile, extention, Japanese utf8 characters, spaces in name'

  for s in . sub1 sub2 sub1/sub3 .hidden_dir
  do

     #note -p not needed as we create dirs top-down
     #fails for "." -- but the hack allows us to use a single loop
     #for creating testdata in all dirs
     mkdir $root_dir/$s
     dir=$root_dir/$s

     i=0
     while [[ $i -lt ${#test_files[*]} ]]
     do
       filename=${test_files[$((i++))]}
       echo ${test_files[$((i++))]} > "$dir/$filename"
     done
   done

   echo "$root_dir"
}

# Run test, given a hash-type as first argument
runtest()
{
  sum=$1

  root_dir=$(mktest)

  echo "created dir: $root_dir"
  echo "Running first test with hashtype $sum:"
  echo
  main $root_dir $sum
  echo
  echo "Running second test:"
  echo
  main $root_dir $sum
  echo "Updating all files:"

  find $root_dir -type f | while read f
  do
    echo "more content" >> "$f"
  done

  echo
  echo "Running final test:"
  echo
  main $root_dir $sum
  #cleanup:
  rm -r $root_dir
}

# Test md5 and whirlpool hashes on generated data.
runtests()
{
  runtest md5
  runtest wp
}

#For in order to be able to call the script recursively, without splitting off
# functions to separate files:
case "$1" in
  'test')
    runtests
  ;;
  'hashname')
    hashname "$2"
  ;;
  'hashmove')
    hashmove "$2"
  ;;
  'run')
    main "$2" "$3"
  ;;
  *)
    echo "Use with: $0 test - or if you just want to try it on a folder:"
    echo "  $0 run path (implies md5)"
    echo "  $0 run md5 path"
    echo "  $0 run wp path"
  ;;
esac

usando zsh:

$ ls
a.txt
b.txt
c.txt

La magia:

$ FILES=**/*(.) 
$ # */ stupid syntax coloring thinks this is a comment
$ for f in $FILES; do hash=`md5sum $f | cut -f1 -d" "`; mv $f "$f:r.$hash.$f:e"; done
$ ls
a.60b725f10c9c85c70d97880dfe8191b3.txt
b.3b5d5c3712955042212316173ccf37be.txt
c.2cd6ee2c70b0bde53fbe6cac3c8b8bb1.txt

deconstrucción feliz!

Edit: añadido archivos en los subdirectorios y comillas argumento mv

Ruby:

#!/usr/bin/env ruby
require 'digest/md5'

Dir.glob('**/*') do |f|
  next unless File.file? f
  next if /\.md5sum-[0-9a-f]{32}/ =~ f
  md5sum = Digest::MD5.file f
  newname = "%s/%s.md5sum-%s%s" %
    [File.dirname(f), File.basename(f,'.*'), md5sum, File.extname(f)]
  File.rename f, newname
end

Maneja los nombres de archivos que tienen espacios, sin extensión, y que ya han sido hash.

Ignora los archivos y directorios ocultos -. File::FNM_DOTMATCH añadir como segundo argumento de glob si eso es deseado

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top