Alfabetisasi kata-kata dalam nama file menggunakan sort?

8

Saat membaca tutorial tentang pengubahan nama file batch di bash dan menggunakan sortperintah untuk mengurutkan konten file, saya belum bisa menemukan cara menggabungkan keduanya.

Saya memiliki direktori yang isinya diurutkan menggunakan tag di dalam nama file, mirip dengan bagaimana program TagSpaces menangani hal-hal. Saya menambahkan tag apa pun yang dapat saya pikirkan sampai akhir nama file ketika saya membuatnya atau mengunduhnya. Ini sebuah contoh:

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

Sekarang saya ingin memeriksa semua file ini dan mengganti nama mereka sehingga tag diurutkan berdasarkan abjad, tanpa mempengaruhi apa pun sebelum atau setelah tag (misalnya judul gambar atau ekstensi file). Jadi di atas akan menjadi:

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

Bagaimana saya mencapai ini? Aku bahkan tidak bisa mencari cara untuk melewati file nama , dan tidak isinya, untuk perintah seperti sort, yang output aku bisa maka mungkin pipa ke mv.

ArdentCertes
sumber
bukan jawaban untuk pertanyaan Anda, tetapi Anda harus memposting jawaban tentang TagSpaces di Menambahkan tag ke file (PDF) dan proses dari baris perintah atau skrip - tampaknya sangat relevan dengan pertanyaan itu pada pandangan pertama.
cas

Jawaban:

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

Pengujian:

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

Jika Anda memiliki berbasis perl rename( prenamepada beberapa sistem) maka Anda dapat melakukan split + mengurutkan tag menggunakan perl. Misalnya diberikan

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

lalu (dengan beberapa pertikaian jelek untuk menghapus dan mengganti .jpgakhiran)

$ 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

Memeriksa

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

Mungkin banyak ruang untuk perbaikan - tapi saya harap ini memberi Anda beberapa ide.

Steeldriver
sumber
3

Dengan zsh:

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

(lepaskan -n(dry-run) jika senang).

  • [^#]#: 0 atau lebih karakter bukan # ( #seperti *di regexps)
  • s: : terpecah pada ruang
  • o: order (sort)
  • j: :: gabung dengan ruang.

Jadi, kami membagi bagian di antara yang pertama #(termasuk) dan terakhir .(dikecualikan) pada ruang, mengurutkan daftar hasil yang kami gabungkan kembali dengan ruang.

Secara rekursif:

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

Untuk mengizinkan spasi dalam nama tag, kami dapat membagi #dan memangkas spasi tambahan, mengurutkan dan bergabung #dengan:

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

Tambahkan (#qD)kualifikasi glob jika Anda juga ingin memproses file tersembunyi ( Datau file) atau ingin memproses file dalam direktori tersembunyi.

Stéphane Chazelas
sumber
2

Pertanyaan bagus!

Berikut ini bashskrip sederhana saya untuk ini:

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

Penjelasan:

  • Di afile=( ${file#*)} );: kami mengonversi string ke array. Dalam keadaan ini shell sedang melakukan pemisahan kata dengan spasi kecuali Anda mengutip string.

    Dalam ${file#*)}( cut-up-to-first-prefix ): kita menanggalkan semuanya dari awal string hingga pertama kali )terlihat menggunakan shell parameter expansionkarena akan menghasilkan #wallpaper #4k #googleimages.jpgmempertimbangkanfile="Sunrise (2) #wallpaper #4k #googleimages.jpg"

  • Di ${file%%#*}( cut-up-to-last-suffix ); pengupasan mulai dari ujung ke pengemis tali sampai terakhir #terlihat. ini akan menghasilkanSunrise (2)

  • Di dalam ${afile[@]%%.*}( cut-up-to-last-suffix ): sama seperti di atas, stripping mulai dari ujung hingga mengemis string (di sini di setiap array yang diindeks) sampai terakhir .terlihat. ini akan menghasilkan #wallpaper #4k #googleimages, kita juga bisa menggunakan ${afile[@]%.*}lebih baik!

  • Di printf "%s\n" "${afile[@]%%.*}": kita mencetak elemen array dengan baris baru ( [@]digunakan untuk array yang diindeks), (mengapa dengan baris baru? Karena kita akan mengurutkannya dan kita harus membagi elemen dalam baris baru)

  • Di $(sort<(printf "%s\n" "${afile[@]%%.*}")): kami menyortir elemen (atau tag).

  • Dalam $(echo $(sort<(printf "%s\n" "${afile[@]%%.*}"))): sama seperti di atas tetapi kami menggunakan echoperintah tambahan untuk mengumpulkan elemen yang diurutkan menjadi satu linier.

    mungkin sama juga dengan menggunakan double xargslike ... |xargs -n1| sort | xargs.
    Silakan lihat contoh di bawah ini untuk lebih memahami langkah ini:

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

Akhirnya pada akhirnya, mvperintahnya adalah mengubah $filenama menjadi nama yang dimodifikasi.

Ps: hapus echo mv ...di depan mvuntuk keluar dari dry-run dan melakukan penggantian nama sebenarnya.

αғsнιη
sumber
1

Tampaknya ini agak terlalu rumit untuk jujur ​​bagi Bash. Bukan tidak mungkin, tetapi menurut saya, Anda jauh lebih baik menggunakan bahasa yang dibuat untuk pemrograman "nyata" di sini. Solusi Bash kemungkinan tidak akan dapat dipertahankan. (Tidak mencoba untuk menghina solusi Bash-only, saya pikir mengubah nama-sihir sangat menakjubkan, sebenarnya)

Yang mengatakan, inilah solusi dengan Ruby. Anda dapat menulisnya ke file dan kemudian jalankan file dari dalam shell Anda, mengingat bahwa Anda telah menginstal 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

Untuk menjalankan skrip, cukup letakkan di direktori tempat gambar Anda berada. Solusi ini harus cukup tangguh untuk nama gambar dan tag konyol seperti C#tidak akan menimbulkan masalah. Yang mengatakan, pastikan untuk membuat cadangan sebelum menjalankan skrip. Operasi gerakan bisa sama destruktifnya dengan a rm.

PawkyPenguin
sumber