Як я можу впорядкувати файли на основі їх першої літери в папках AZ


15

Я шукаю спосіб (бажано термінал) організувати понад 1000 шрифтів за першим листом.

В основному створюйте каталоги, A-Z, #а потім переміщуйте файли шрифтів у ці каталоги на основі першого символу імені файлу. Шрифти, що починаються з цифр [0-9] або інших спеціальних символів, які потрібно перемістити до #каталогу.


Ви хочете, щоб каталоги були створені, навіть якщо немає файлів, що починаються з цього листа?
Arronical

@Arronical Nope. Просто, якщо є файли.
Парто

4
Я сподіваюся , що ця посилання допоможе вам stackoverflow.com/questions/1251938 / ...
Karthickeyan

Відповіді:


13

Варіант пізнього пітона:

#!/usr/bin/env python3
import os
import sys
import shutil

def path(dr, f): return os.path.join(dr, f)

dr = sys.argv[1]
for f in os.listdir(dr):
    fsrc = path(dr, f)
    if os.path.isfile(fsrc):
        s = f[0]; target = path(dr, s.upper()) if s.isalpha() else path(dr, "#")
        if not os.path.exists(target):
            os.mkdir(target)
        shutil.move(fsrc, path(target, f))

Як користуватись

  1. Скопіюйте скрипт у порожній файл, збережіть його як move_files.py
  2. Запустіть його в каталозі як аргумент:

    python3 /path/to/move_files.py /path/to/files
    

Сценарій створить (суб) каталог (-ies) (верхній регістр) лише тоді, коли він дійсно потрібен

Пояснення

Сценарій:

  • перераховує файли, отримує перший символ (визначає вихідний шлях):

    for f in os.listdir(dr):
        s = f[0]; fsrc = path(dr, f)
  • перевіряє, чи елемент є файлом:

    if os.path.isfile(fsrc):
  • визначає цільову папку для того, якщо перший символ є альфа або ні:

    target = path(dr, s.upper()) if s.isalpha() else path(dr, "#")
  • перевіряє, чи папка вже існує чи ні, створює її, якщо ні:

    if not os.path.exists(target):
        os.mkdir(target)
  • переміщує елемент у відповідну папку:

    shutil.move(fsrc, path(target, f))

Гей, Якову. Чи є чек для великих літер?
Парто

@Parto Абсолютно! Яким способом це вам потрібно? (Я можу перевірити 3,5 години викладання :)
Яків Влійм

Це було насправді. Ідеальна реалізація.
Парто

Розглянувши, я вирішив піти з цією відповіддю з кількох причин: 1). Пояснення того, що відбувається. 2). Відповідь виконана правильно з першої спроби
Парто

11

Код з гольфу, але читабельний, лише з двома командами та двома регулярними виразами:

mkdir -p '#' {a..z}
prename -n 's|^[[:alpha:]]|\l$&/$&|; s|^[0-9]|#/$&|' [[:alnum:]]?*

Якщо у вас є величезна кількість файлів для переміщення, занадто багато, щоб вписатись у список аргументів процесу (так, існує обмеження, і це може бути лише кілька кілобайт), ви можете генерувати список файлів за допомогою іншої команди та команди, що prename, наприклад:

find -mindepth 1 -maxdepth 1 -name '[[:alnum:]]?*' -printf '%f\n' |
prename -n 's|^[[:alpha:]]|\l$&/$&|; s|^[0-9]|#/$&|'

Це має додаткову перевагу в тому, що не намагаються перемістити ім'я прямого файлу, [[:alnum:]]?*якщо жоден файл не відповідає глобальній схемі. findтакож дозволяє набагато більше критеріїв відповідності, ніж обшивка оболонки. Альтернативою є встановлення параметра nullglobоболонки та закриття стандартного вхідного потоку prename. 1

В обох випадках зніміть -nперемикач, щоб насправді перемістити файли, а не просто показати, як вони будуть переміщені.

Додаток: Ви можете знову видалити порожні каталоги за допомогою:

rmdir --ignore-fail-on-non-empty '#' {a..z}

1 shopt -s nullglob; prename ... <&-


8

Якщо ви не заперечуєте zsh, функція та пара zmvкоманд:

mmv() {echo mkdir -p "${2%/*}/"; echo mv -- "$1" "$2";}
autoload -U zmv
zmv -P mmv '([a-zA-Z])(*.ttf)' '${(UC)1}/$1$2'
zmv -P mmv '([!a-zA-Z])(*.ttf)' '#/$1$2'

mmvФункція робить каталог і переміщує файл. zmvпотім забезпечує узгодження шаблонів і підстановку. По-перше, переміщення імен файлів, починаючи з алфавіту, потім все інше:

$ zmv -P mmv '([a-zA-Z])(*.ttf)' '${(UC)1}/$1$2'
mkdir -p A/
mv -- abcd.ttf A/abcd.ttf
mkdir -p A/
mv -- ABCD.ttf A/ABCD.ttf
$ zmv -P mmv '([!a-zA-Z])(*.ttf)' '#/$1$2'
mkdir -p #/
mv -- 123.ttf #/123.ttf
mkdir -p #/
mv -- 七.ttf #/七.ttf

Виконайте ще раз без echoв mmvвизначенні «S насправді виконати рух.


8

Мені не вдалося розібратися з гарним способом зробити великі регістри імен каталогів (або перемістити файли з великої літери), хоча ви могли це зробити згодом за допомогою rename...

mkdir {a..z} \#; for i in {a..z}; do for f in "$i"*; do if [[ -f "$f" ]]; then echo mv -v -- "$f" "$i"; fi; done; done; for g in [![:alpha:]]*; do if [[ -f "$g" ]]; then echo mv -v -- "$g" \#; fi; done

або більш зрозуміло:

mkdir {a..z} \#; 
for i in {a..z}; do 
  for f in "$i"*; do
    if [[ -f "$f" ]]; then 
      echo mv -v -- "$f" "$i"; 
    fi 
  done
done
for g in [![:alpha:]]*; do 
  if [[ -f "$g" ]]; then 
    echo mv -v -- "$g" \#
  fi
done

Видаліть echoпісля тестування дійсне переміщення файлів

І потім

rename -n 'y/[a-z]/[A-Z]/' *

видаліть, -nякщо це виглядає добре після тестування, і запустіть знову.


2
Можна використовувати if [[ -d "${i^}" ]]для внесення змінного iкапіталу і mkdir {A..Z}на початку.
Arronical

Дозвольте спробувати це і побачити
Парто

@Arronical спасибі! Я покину його, оскільки ви розмістили його по-своєму
Занна

@Zanna Мені подобається, що ми приходили до нього з різних напрямків, перебираючи букви та використовуючи їх як критерії пошуку, мені ніколи не приходило. Я впевнений, що є розумне і швидке рішення з пошуку, але я не можу обійти його!
Arronical

Привіт, Занна, це не перемістило шрифти, які починаються з великої літери. Інакше добре працював.
Парто

7

Наступні команди в каталозі, що містить шрифти, повинні працювати, якщо ви хочете використовувати за межами каталогу зберігання шрифтів, змінити for f in ./*на for f in /directory/containing/fonts/*. Це дуже оболонковий метод, настільки повільний, а також нерекурсивний. Це створить лише каталоги, якщо є файли, які починаються із відповідного символу.

target=/directory/to/store/alphabet/dirs
mkdir "$target"
for f in ./* ; do 
  if [[ -f "$f" ]]; then 
    i=${f##*/}
    i=${i:0:1}
    dir=${i^}
    if [[ $dir != [A-Z] ]]; then 
      mkdir -p "${target}/#" && mv "$f" "${target}/#"
    else
      mkdir -p "${target}/$dir" && mv "$f" "${target}/$dir"
    fi
  fi
done

Як один вкладиш, знову з каталогу зберігання шрифтів:

target=/directory/to/store/alphabet/dirs; mkdir "$target" && for f in ./* ; do if [[ -f "$f" ]]; then i=${f##*/}; i=${i:0:1} ; dir=${i^} ; if [[ $dir != [A-Z] ]]; then mkdir -p "${target}/#" && mv "$f" "${target}/#"; else mkdir -p "${target}/$dir" && mv "$f" "${target}/$dir" ; fi ; fi ; done

Метод, що використовує find, з аналогічною обробкою рядків, використовуючи розширення параметра bash, яке буде рекурсивним і має бути дещо швидшим, ніж чиста версія оболонки:

find . -type f -exec bash -c 'target=/directory/to/store/alphabet/dirs ; mkdir -p "$target"; f="{}" ; i="${f##*/}"; i="${i:0:1}"; i=${i^}; if [[ $i = [[:alpha:]] ]]; then mkdir -p "${target}/$i" && mv "$f" "${target}/$i"; else mkdir -p "${target}/#" && mv "$f" "${target}/#"; fi' \;

Або ще читати:

find . -type f -exec bash -c 'target=/directory/to/store/alphabet/dirs 
   mkdir -p "$target"
   f="{}"
   i="${f##*/}"
   i="${i:0:1}"
   i=${i^}
   if [[ $i = [[:alpha:]] ]]; then 
      mkdir -p "${target}/$i" && mv "$f" "${target}/$i"
   else
      mkdir -p "${target}/#" && mv "$f" "${target}/#"
   fi' \;

Цей теж працював. Навіть для великих і малих літер.
Парто

5

Карта кожного файлу на ім'я каталогу з допомогою tr, а потім mkdirі mv:

find /src/dir -type f -print0 |
xargs -0 -I{} bash -c \
  'dir=/dest/$(basename "{}" | cut -c1 | tr -C "a-zA-Z\n" "#" | tr "a-z "A-Z"); mkdir -p $dir; mv "{}" $dir'

Мені це дуже подобається, це узгоджується з тим, що я сприймав із своїм бажанням використовувати знахідку. Чи є спосіб створити лише імена верхнього регістру регістрів та перемістити в них назви файлів нижнього регістру?
Arronical

1
Я додав ще одну trдля перетворення у великі регістри.
хн.

Чому об’їзд через xargsпросто дзвонити bashзнову? Хіба не було б простіше і набагато легше читати передачу виводу findв цикл while, а readтам - запис за записом?
Девід Фоерстер

Добре запитання, @DavidFoerster. Я думаю, що упередження щодо циклів в одноклапах і перевагу більш функціонального підходу. Але сценарій bash в рядку теж не дуже елегантний, тому я б сказав, що циклічнаwhile версія ( bit.ly/2j2mhyb ), можливо, краща.
хн.

До речі, ви можете уникнути неприємну {}заміну , якщо ви дозволите xargsAppend аргумент , а потім зверніться до $1всередині скрипта, наприклад: xargs -0 -n1 -- bash -c 'dir=/dest/$(basename "$1" | ...); ...; mv "$1" "$dir"' _. (Майте на увазі фінал _!)
Девід Фоерстер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.