Алфавитные слова в именах файлов с помощью сортировки?

8

Читая руководства по пакетному переименованию файлов в bash и используя sortкоманду для сортировки содержимого файла, я не смог понять, как их объединить.

У меня есть каталог, содержимое которого отсортировано с помощью тегов в имени файла, подобно тому, как программа TagSpaces обрабатывает вещи. Я добавляю любые теги, которые могу придумать, в конец имени файла при его создании или загрузке. Вот пример:

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

Теперь я хочу просмотреть все эти файлы и переименовать их, чтобы теги сортировались в алфавитном порядке, не затрагивая ничего до или после тегов (например, заголовок изображения или расширение файла). Таким образом, вышесказанное станет:

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

Как мне это сделать? Я даже не могу понять, как передать имя файла , а не его содержимое, в такую ​​команду sort, чей вывод я мог бы затем передать mv.

ArdentCertes
источник
не ответ на ваш вопрос, но вы должны опубликовать ответ о TagSpaces в разделе Добавление тегов в файлы (PDF) и обрабатывать их из командной строки или сценария - на первый взгляд это кажется очень уместным для этого вопроса.
Cas

Ответы:

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

Тестирование:

##### 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
источник
3

Если у вас есть perl-based rename( prenameв некоторых системах), вы можете сделать split + сортировать теги, используя perl. Например, учитывая

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

затем (с каким-то уродливым спором убрать и заменить .jpgсуффикс)

$ 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

проверка

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

Вероятно, есть много возможностей для улучшения, но я надеюсь, что это даст вам некоторое представление.

steeldriver
источник
3

С zsh:

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

(удалить -n(всухую), если счастлив).

  • [^#]#: 0 или более не # символов ( #как *в регулярных выражениях)
  • s: : разделить на пространство
  • o: заказ (сортировка)
  • j: :: присоединиться к пробелу

Итак, мы разделяем часть между первым #(включенным) и последним .(исключенным) в пространстве, сортируем результирующий список, который мы объединяем с пробелом.

Рекурсивный:

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

Чтобы разрешить пробелы в именах тегов, мы можем разделить #и обрезать конечные пробелы, отсортировать и объединить #с помощью:

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

Добавьте (#qD)спецификатор glob, если вы также хотите обрабатывать скрытые файлы ( Dot файлы) или хотите обрабатывать файлы в скрытых каталогах.

Стефан Шазелас
источник
2

Хороший вопрос!

Вот мой простой bashскрипт для этого:

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

Объяснение:

  • В afile=( ${file#*)} );: мы конвертируем строку в массив. В этом состоянии оболочка выполняет разбиение слов на пробелы, если вы не заключите строку в кавычки.

    В ${file#*)}( cut-up-to-first-prefix ): мы убираем все с начала строки до первого, )увиденного, используя, shell parameter expansionпоскольку это приведет к #wallpaper #4k #googleimages.jpgрассмотрениюfile="Sunrise (2) #wallpaper #4k #googleimages.jpg"

  • В ${file%%#*}( сокращение до последнего суффикса ); зачистка начала с конца до начала строки до последнего #раза. это приведетSunrise (2)

  • In ${afile[@]%%.*}( cut-up-to-last-суффикс ): то же, что и выше, разделение начинается с конца до начала строки (здесь в каждом индексированном массиве) до последнего .увиденного. это приведет #wallpaper #4k #googleimages, мы могли бы также использовать ${afile[@]%.*}лучше!

  • В printf "%s\n" "${afile[@]%%.*}": мы печатаем элементы массива с символами новой строки ( [@]используется для индексированного массива), (почему с символами новой строки? Потому что мы отсортируем их, и мы должны разбить элементы на новые строки)

  • В $(sort<(printf "%s\n" "${afile[@]%%.*}")): мы сортируем элементы (или теги).

  • In $(echo $(sort<(printf "%s\n" "${afile[@]%%.*}"))): то же, что и выше, но мы использовали дополнительную echoкоманду для объединения отсортированных элементов в одну линейную.

    возможно то же самое с использованием двойного xargsлайка ... |xargs -n1| sort | xargs.
    Пожалуйста, смотрите ниже пример, чтобы лучше понять этот шаг:

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

Наконец, в конце mvкоманда переименовывает $fileв измененное имя.

Ps: удалить echo mv ...перед mvвыходом из пробного прогона и выполнить фактическое переименование.

αғsнιη
источник
1

Это кажется слишком сложным для Bash, чтобы быть честным. Это не невозможно, но, на мой взгляд, вам гораздо лучше использовать язык, созданный для «настоящего» программирования. Решение Bash, скорее всего, будет неприемлемым. (Не пытаясь оскорбить решение Bash-only, я думаю, что волшебство переименования на самом деле довольно удивительно)

Тем не менее, вот решение с Руби. Вы можете записать его в файл, а затем просто запустить файл из вашей оболочки, учитывая, что у вас установлен 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

Чтобы выполнить сценарий, просто поместите его в каталог, в котором находятся ваши изображения. Это решение должно быть достаточно устойчивым к глупым именам изображений и таким тегам, C#которые не вызовут проблем. Тем не менее, не забудьте сделать резервную копию перед запуском скрипта. Операции движения могут быть столь же разрушительными, как и rm.

PawkyPenguin
источник