¿Alfabetizar palabras dentro de nombres de archivo usando sort?

8

Al leer los tutoriales sobre el cambio de nombre de los archivos por lotes en bash y usar el sortcomando para ordenar el contenido del archivo, no he podido descubrir cómo combinar los dos.

Tengo un directorio cuyo contenido está ordenado usando etiquetas dentro del nombre del archivo, similar a cómo el programa TagSpaces maneja las cosas. Agrego las etiquetas que se me ocurren al final del nombre de archivo cuando lo creo o lo descargo. Aquí hay un ejemplo:

Sunrise (2) #wallpaper #4k #googleimages.jpg

Ahora quiero revisar todos estos archivos y cambiarles el nombre para que las etiquetas se ordenen alfabéticamente, sin afectar nada antes o después de las etiquetas (por ejemplo, el título de una imagen o la extensión del archivo). Entonces lo anterior se convertiría en:

Sunrise (2) #4k #googleimages #wallpaper.jpg

¿Cómo logro esto? Ni siquiera puedo imaginar cómo pasar el nombre de un archivo , y no su contenido, a un comando como sort, cuya salida podría canalizar mv.

ArdentCertes
fuente
no es una respuesta a su pregunta, pero debe publicar una respuesta sobre TagSpaces en Agregar etiquetas a archivos (PDF) y procesar desde la línea de comandos o script : parece muy relevante a esa pregunta a primera vista.
cas

Respuestas:

5
#!/bin/bash

for i in dir_for_files/*; do
    filename=${i%%#*}
    sorted_tags=$(grep -o "#[^ .]*" <<< "$i" | sort -n | tr '\n' ' ')
    ext=${i#*.}
    echo mv "$i" "${filename}${sorted_tags% }.$ext"
done

Pruebas:

##### Before sorting #####    
$ ls -1 dir_for_files
Note (3) #textfile #notes #important.txt
Sunrise (2) #wallpaper #4k #googleimages.jpg
Sunset (2) #wallpaper #2k #images.jpg

$ ./sort_tags.sh

##### After sorting #####
$ ls -1 dir_for_files
Note (3) #important #notes #textfile.txt
Sunrise (2) #4k #googleimages #wallpaper.jpg
Sunset (2) #2k #images #wallpaper.jpg
MiniMax
fuente
3

Si tiene el basado en perl rename( prenameen algunos sistemas), entonces podría hacer una división + ordenar las etiquetas usando perl. Por ejemplo, dado

$ ls *.jpg
Sunrise (2) #wallpaper #4k #googleimages.jpg

luego (con algunas disputas feas para eliminar y reemplazar el .jpgsufijo)

$ rename -v 'my @F = split / #/, substr($_,0,-4); $_ = (join " #", shift @F, sort @F) . ".jpg"' *.jpg
Sunrise (2) #wallpaper #4k #googleimages.jpg renamed as Sunrise (2) #4k #googleimages #wallpaper.jpg

Comprobación

$ ls *.jpg
Sunrise (2) #4k #googleimages #wallpaper.jpg

Probablemente hay mucho margen de mejora, pero espero que te dé alguna idea.

conductor de acero
fuente
3

Con zsh:

autoload zmv # best in ~/.zshrc
zmv -n '([^#]#)(\#*)(.*)' '$1${(j: :)${(os: :)2}}$3'

(eliminar el -n(marcha en seco) si está satisfecho)

  • [^#]#: 0 o más caracteres que no son # ( #es como *en expresiones regulares)
  • s: : dividido en el espacio
  • o: orden (ordenar)
  • j: :: únete con el espacio.

Entonces, estamos dividiendo la parte entre la primera #(incluida) y la última .(excluida) en el espacio, ordenamos la lista resultante que unimos nuevamente con el espacio.

Recursivamente:

zmv -n '(**/)([^#]#)(\#*)(.*)' '$1$2${(j: :)${(os: :)3}}$4'

Para permitir espacios en los nombres de etiquetas, podríamos dividir #y recortar espacios finales, ordenar y unir #con:

zmv -n '([^#]#\#)(*)(.*)' '$1${(j: #:)${(os:#:)2}%% #}$3'

Agregue un (#qD)calificador global si también desea procesar archivos ocultos ( Dot archivos) o si desea procesar archivos en directorios ocultos.

Stéphane Chazelas
fuente
2

¡Buena pregunta!

Aquí está mi bashscript simple para esto:

for file in *.jpg; do 
    afile=( ${file#*)} ); 
    echo mv "$file" "${file%%#*}$(echo $(sort<(printf "%s\n" "${afile[@]%%.*}"))).jpg";
done

Explicación:

  • En afile=( ${file#*)} );: estamos convirtiendo la cadena en una matriz. En este estado, el shell realiza la división de palabras con espacios a menos que cite la cadena.

    En ${file#*)}( cut-up-to-first-prefix ): estamos quitando todo desde el comienzo de la cadena hasta que se )ve por primera vez, shell parameter expansionya que resultará #wallpaper #4k #googleimages.jpgteniendo en cuenta elfile="Sunrise (2) #wallpaper #4k #googleimages.jpg"

  • En ${file%%#*}( corte hasta el último sufijo ); pelar desde el principio hasta el principio de la cuerda hasta la última #vez que se ve. esto resultaráSunrise (2)

  • En ${afile[@]%%.*}( cut-up-last-last-sufijo ): igual que el anterior, el despojo comienza desde el final hasta el comienzo de la secuencia (aquí en cada matriz indexada) hasta la última .vez que se ve. esto resultará #wallpaper #4k #googleimages, ¡también podríamos usar ${afile[@]%.*}mejor!

  • En printf "%s\n" "${afile[@]%%.*}": estamos imprimiendo los elementos de la matriz con líneas nuevas ( [@]utilizadas para la matriz indexada), (¿por qué con las líneas nuevas? Porque las clasificaremos y deberíamos dividir los elementos en líneas nuevas)

  • En $(sort<(printf "%s\n" "${afile[@]%%.*}")): estamos ordenando los elementos (o etiquetas).

  • En $(echo $(sort<(printf "%s\n" "${afile[@]%%.*}"))): igual que el anterior pero usamos un echocomando adicional para reunir los elementos ordenados en uno lineal.

    posible lo mismo también con usar doble xargslike ... |xargs -n1| sort | xargs.
    Consulte el siguiente ejemplo para comprender mejor este paso:

    echo -e "C\n4\nB" |sort
    4
    B
    C
    echo $(echo -e "C\n4\nB" |sort)
    4 B C
    

Finalmente, al final, el mvcomando está cambiando el nombre $fileal nombre modificado que debería ser.

Ps: eliminar echo mv ...delante del mvpara salir de la ejecución en seco y realizar un cambio de nombre real.

αғsнιη
fuente
1

Esto parece demasiado complicado para que Bash sea sincero. No es imposible, pero en mi opinión, es mucho mejor usar un lenguaje hecho para programación "real" aquí. Es probable que una solución Bash sea imposible de mantener. (No estoy tratando de insultar la solución solo Bash, creo que la magia de cambio de nombre es bastante sorprendente, en realidad)

Dicho esto, aquí hay una solución con Ruby. Puede escribirlo en un archivo y luego simplemente ejecutar el archivo desde su shell, dado que tiene instalado Ruby.

#!/usr/bin/env ruby
Dir.glob('*.jpg').each do |filename|
    # `name` contains the name of the file, without the tags. `tags` is an array
    # of all tags of the file.
    name, *tags = filename.split(' #')
    # if there aren't any tags, just skip the file.
    if tags.empty?
        next
    end
    # remove the trailing '.jpg' and sort all the tags
    tags.last.gsub!(/\.jpg$/,'')
    tags.sort!
    tags = [name] + tags
    # finally, move the file to the correct location with sorted tags.
    File.rename filename, "#{tags.join(' #')}.jpg"
end

Para ejecutar el script, simplemente colóquelo en el directorio donde se encuentran sus imágenes. Esta solución debería ser bastante resistente a los nombres y etiquetas de imágenes tontas, ya C#que no causarán problemas. Dicho esto, asegúrese de hacer una copia de seguridad antes de ejecutar el script. Las operaciones de movimiento pueden ser tan destructivas como a rm.

PawkyPenguin
fuente