У меня есть несколько тысяч файлов в одном каталоге, как который я хотел бы сопоставить в каталогах так:
От этого:
└── Files
├── AAA.mkv
├── AAA.nfo
├── AAA-picture.jpg
├── BBB.mp4
├── BBB.srt
├── BBB-clip.mp4
├── CCC.avi
├── CCC.srt
├── CCC-clip.mov
└── CCC.nfo
К этому:
└── Files
├── AAA
│ ├── AAA.mkv
│ ├── AAA.nfo
│ └── AAA-picture.jpg
├── BBB
│ ├── BBB.mp4
│ ├── BBB.srt
│ └── BBB-clip.mp4
└── CCC
├── CCC.avi
├── CCC.srt
├── CCC-clip.mov
└── CCC.nfo
Имена файлов варьируются по длине и количеству слов, иногда разделяемых пробелами и возможно некоторыми с дефисами (в дополнение к тем заканчивающимся '-short'. Они - прежде всего, видеофайлы со множеством форматов/контейнеров: mov/mpg/mkv/mp4/avi/ogg. Некоторые снабжены субтитрами. У некоторых есть файлы со связанными метаданными (.nfo или - клип)
Править: Основные файлы являются видео (это - то, где я хотел бы потянуть имя каталога). Связанные файлы представляют метаданные. Некоторые отличающиеся в именовании только расширением. Существует полдюжина других вариаций на основное имя файла как-clip.mp4-clip.mov или -picture.jpg, который я изобразил, было ли что-то предложено с теми немногих затем, я мог бы (надо надеяться), работать при выяснении остальных. Таким образом, AAA.mkv перемещается в каталог под названием AAA. Затем все файлы метаданных, которые начинаются с AAA, присоединяются к нему (т.е. в этом примере: AAA-picture.jpg и AAA.nfo). Таким образом, базовое имя является на самом деле подстрокой в случае файла AAA-picture.jpg. Я сказал бы, что, вероятно, относительно безопасно просто использовать дефис в качестве фактора разграничивания..., хотя '-клип' или '-изображение' в целом были бы более безопасными.
Как я могу сделать это, не заболев синдромом канала запястья? Я посмотрел на это, но это достаточно отличалось, что шипели мои слабые способности к сценариям.
Спасибо.
В то время как Ваш вопрос отмечен с bash
, это было бы несколько неприятно (по моему скромному мнению) для использования bash
для такой задачи. Я предложил бы использовать Python, потому что он имеет много хороших функций для сложных задач, и этот ответ предоставляет решение с помощью того языка.
По существу, что происходит, вот то, что мы используем regex для разделения имен файлов в нескольких разделителях, получаем только первую часть и используем уникальный набор тех первых частей как базовые имена для новых каталогов.
Мы затем пересекаем главный каталог снова и сортируем файлы в их соответствующих местах.
Сценарий не делает ничего захватывающего, и на самом деле в анализе алгоритма это не также успело бы из-за вложенного для циклов, но для "быстрого и грязного, все же осуществимого" решения это в порядке. Если Вам интересно, что делает каждая строка, существует много комментариев, добавленных для объяснения функциональности
Отметьте, демонстрация только показывает печать новых имен файлов для тестирования цели только. Не прокомментируйте os.rename()
часть для фактического перемещения файла.
bash-4.3$ # Same directory structure as in OP example
bash-4.3$ ls TESTDIR
bash-4.3$ # now run script
AAA AAA.mkv AAA.nfo AAA-picture.jpg BBB BBB-clip.mp4 BBB.mp4 BBB.srt
bash-4.3$ ./collate_files.py ./TESTDIR
/home/xieerqi/TESTDIR/AAA/AAA-picture.jpg
/home/xieerqi/TESTDIR/AAA/AAA.mkv
/home/xieerqi/TESTDIR/AAA/AAA.nfo
/home/xieerqi/TESTDIR/BBB/BBB.srt
/home/xieerqi/TESTDIR/BBB/BBB.mp4
/home/xieerqi/TESTDIR/BBB/BBB-clip.mp4
#!/usr/bin/env python
import re,sys,os
top_dir = os.path.realpath(sys.argv[1])
# Create list of items in directory first
# splitting names at multiple separators
dir_list = [os.path.join(top_dir,re.split("[.-]",f)[0])
for f in os.listdir(top_dir)
]
# Creating set ensures we will have unique
# directory namings
dir_set = set(dir_list)
# Make these directories first
for dir in dir_set:
if not os.path.exists(dir):
os.mkdir(dir)
# now get all files only, no directories
files_list = [f for f in os.listdir(top_dir)
if os.path.isfile(os.path.join(top_dir,f))
]
# Traverse lists of directories and files,
# check if a filename starts with directory
# that we're testing now, and if it does - move
# the file to that directory
for dir in dir_set:
id_string = os.path.basename(dir)
for f in files_list:
filename = os.path.basename(f)
if filename.startswith(id_string):
new_path = os.path.join(dir,filename)
print(new_path)
#os.rename(f,new_path)
re.split()
функция): добавьте внутренние квадратные скобки (значение "[.-]"
) добавьте любые символы, которые Вы хотите.os.rename()
функция. Кроме того, Вы могли import shutil
и используйте shutil.move()
функция. См. https://stackoverflow.com/a/8858026/3701431Я сделал маленький сценарий удара, чтобы сделать это, упрощенное и улучшенное благодаря комментариям от OP, @dannysauer, @Arronical и @Scott
#!/bin/bash
for file in *
do mkdir -p "${file%%[.-]*}" 2>/dev/null
if [[ -d "${file%%[.-]*}" ]]; then
if [[ -f "$file" ]]; then
echo mv -v -- "$file" "${file%%[.-]*}"
fi
fi
done
Выполненный с echo
сначала и затем удалите echo
на самом деле перемещать файлы. Скрипт должен быть запущен из каталога, где Вы хотите переместить файлы. Если Вы предпочитаете, здесь это как короткая команда:
for file in *; do mkdir -p "${file%%[.-]*}"; if [[ -d "${file%%[.-]*}" ]]; then if [[ -f "$file" ]]; then echo mv -v -- "$file" "${file%%[.-]*}"; fi ; fi ; done
(снова, удалить echo
после тестирования)
for file in *; do mkdir -p "${file%%[.-]*}"
сделайте каталог с названием первой части названия каждого файла (пока первый дефис или точечный символ) -p
флаг очень важен здесь - без него, сценарий переместит только первый файл соответствия (благодаря Arronical для указания на это -p
остановится mkdir
от попытки создать существующие каталоги и жалобу на это)2>/dev/null
сценарий жалуется, что не может создать каталог с тем же именем как самим (но все еще работает), таким образом, мы выбрасываем ошибку - это не необходимо при выполнении как острота if [[ -d "${file%%[.-]*}" ]]; then
если существует каталог с тем именем (если mkdir
было успешно), затем... if [[ -f "$file" ]]
если мы имеем дело с файлом (не каталог или что-то еще) затем... mv -v -- "$file" "${file%%[.-]*}"
переместите его в каталог соответствия.На маленьком сценарии Python:
#!/usr/bin/env python3
import shutil
import os
import sys
dr = sys.argv[1]
for f in os.listdir(dr):
split = f.rfind("."); short = f.find("-")
if split != -1:
extension = f[split:]
newname = f[:short] if short != -1 else f[:split]
target = os.path.join(dr, newname)
if not os.path.exists(target):
os.mkdir(target)
shutil.move(os.path.join(dr, f), os.path.join(target, f))
Использовать его:
move_into.py
Выполните его с каталогом как аргумент:
python3 /path/to/move_into.py /path/to/directory
Сценарий предполагает, что все (соответствующие) файлы имеют расширения. Если файл не имеет никакого расширения, ничего не происходит с ним. Если это - проблема, упомяните, может быть изменен легко.
Впоследствии, файл перемещен в соответствующую папку.