Alfabetize as palavras nos nomes de arquivos usando a classificação?

8

Ao ler os tutoriais sobre renomeação de arquivos em lote no bash e usar o sortcomando para classificar o conteúdo do arquivo, não consegui descobrir como combinar os dois.

Eu tenho um diretório cujo conteúdo é classificado usando tags no nome do arquivo, semelhante à maneira como o programa TagSpaces lida com as coisas. Eu adiciono as tags que consigo pensar ao final do nome do arquivo quando o crio ou faço o download. Aqui está um exemplo:

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

Agora eu quero passar por todos esses arquivos e renomeá-los para que as tags sejam classificadas em ordem alfabética, sem afetar nada antes ou depois das tags (por exemplo, o título da imagem ou a extensão do arquivo). Portanto, o acima se tornaria:

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

Como eu faço isso? Eu não consigo nem descobrir como passar o nome de um arquivo , e não o conteúdo, para um comando como sort, cuja saída eu talvez pudesse direcionar mv.

ArdentCertes
fonte
não é uma resposta para sua pergunta, mas você deve postar uma resposta sobre os TagSpaces em Adicionando tags a arquivos (PDFs) e processar a partir da linha de comando ou do script - parece muito relevante para essa pergunta à primeira vista.
6283

Respostas:

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

Teste:

##### 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
fonte
3

Se você possui o baseado em perl rename( prenameem alguns sistemas), pode fazer uma divisão + classificar as tags usando perl. Por exemplo, dado

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

então (com algumas discussões feias para remover e substituir o .jpgsufixo)

$ 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

Verificando

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

Provavelmente, há muito espaço para melhorias - mas espero que isso lhe dê alguma idéia.

chave de aço
fonte
3

Com zsh:

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

(remova o -n(funcionamento a seco) se estiver feliz).

  • [^#]#: 0 ou mais caracteres não # ( #é como *nos regexps)
  • s: : dividir no espaço
  • o: ordem (classificação)
  • j: :: junte-se ao espaço.

Então, estamos dividindo a parte entre o primeiro #(incluído) e o último .(excluído) no espaço, classifique a lista resultante à qual juntamos novamente o espaço.

Recursivamente:

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

Para permitir espaços nos nomes de tags, poderíamos dividir #e aparar espaços finais, classificar e juntar-se a #:

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

Adicione um (#qD)qualificador global se você também deseja processar arquivos ocultos ( Darquivos ot) ou deseja processar arquivos em diretórios ocultos.

Stéphane Chazelas
fonte
2

Boa pergunta!

Aqui está o meu bashscript simples para isso:

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

Explicação:

  • In afile=( ${file#*)} );: estamos convertendo a string em uma matriz. Nesse estado, o shell realiza a divisão de palavras com espaços, a menos que você cite a string.

    Em ${file#*)}( recortar para o primeiro prefixo ): estamos removendo tudo do início da string até a primeira )vez que a usamos, shell parameter expansionpois isso resultará #wallpaper #4k #googleimages.jpgconsiderando ofile="Sunrise (2) #wallpaper #4k #googleimages.jpg"

  • In ${file%%#*}( corte até o último sufixo ); decapagem começa do fim ao começo da corda até ser #visto pela última vez. isso resultaráSunrise (2)

  • Em ${afile[@]%%.*}( corte até o último sufixo ): o mesmo que acima, a remoção começa do fim ao início da cadeia (aqui em cada matriz indexada) até a última .vez que é vista. isso resultará #wallpaper #4k #googleimages, também poderíamos usar ${afile[@]%.*}melhor!

  • In printf "%s\n" "${afile[@]%%.*}": estamos imprimindo os elementos da matriz com novas linhas ( [@]usadas para a matriz indexada), (por que com novas linhas? Porque as classificaremos e devemos dividir os elementos em novas linhas)

  • In $(sort<(printf "%s\n" "${afile[@]%%.*}")): estamos classificando os elementos (ou tags).

  • Em $(echo $(sort<(printf "%s\n" "${afile[@]%%.*}"))): o mesmo que acima, mas usamos extraecho comando para reunir os elementos classificados no linear.

    possível mesmo também com o uso de duplo xargsgosto ... |xargs -n1| sort | xargs.
    Veja o exemplo abaixo para entender melhor esta etapa:

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

Finalmente, no final, o mvcomando está renomeando o$file para o nome modificado que deveria ser.

Ps: remova echo mv ...na frente do mvpara sair a seco e execute a renomeação real.

αғsнιη
fonte
1

Parece um pouco complicado para Bash ser honesto. Não é impossível, mas na minha opinião, você está muito melhor usando uma linguagem criada para programação "real" aqui. Uma solução Bash provavelmente não pode ser mantida. (Não tentando insultar a solução apenas do Bash, acho que a mágica de renomear é realmente incrível)

Dito isto, aqui está uma solução com Ruby. Você pode gravá-lo em um arquivo e, em seguida, apenas executá-lo no seu shell, desde que você tenha o Ruby instalado.

#!/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 executar o script, basta colocá-lo no diretório em que suas imagens estão. Essa solução deve ser bastante resiliente a nomes e marcas de imagens tolas, como C#não causará problemas. Dito isto, certifique-se de fazer um backup antes de executar o script. As operações de movimento podem ser tão destrutivas quanto a rm.

PawkyPenguin
fonte