Группировать файлы в несколько папок

12

У меня есть несколько файлов с различными расширениями , такими как *.pdf, *.mp3, *.jpgи несколько других. Все они хранятся в parentкаталоге.

Как я могу получить список всех расширений, создать несколько папок на основе этих расширений и затем переместить все файлы в соответствующие папки?

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

Ответы:

13

Скрипт Python ниже делает свою работу. Скрытые файлы хранятся отдельно в папке, а также файлы без расширения.

Поскольку он может использоваться для более широкого диапазона целей, я добавил несколько вариантов:

  • Вы можете установить расширения, которые вы хотите исключить из «реорганизации». Если вы просто хотите переместить все, установитеexclude = ()
  • Вы можете выбрать, что делать с пустыми папками ( remove_emptyfolders = Trueили False)
  • Если вы хотите скопировать файлы вместо их перемещения , замените строку:
shutil.move(subject, new_dir+"/"+name)

по:

shutil.copy(subject, new_dir+"/"+name) 

Сценарий:

#!/usr/bin/env python3

import os
import subprocess
import shutil

# --------------------------------------------------------
reorg_dir = "/path/to/directory_to_reorganize"
exclude = (".jpg") # for example
remove_emptyfolders = True
# ---------------------------------------------------------

for root, dirs, files in os.walk(reorg_dir):
    for name in files:
        subject = root+"/"+name
        if name.startswith("."):
            extension = ".hidden_files"
        elif not "." in name:
            extension = ".without_extension"
        else:
            extension = name[name.rfind("."):]
        if not extension in exclude:
            new_dir = reorg_dir+"/"+extension[1:]
            if not os.path.exists(new_dir):
                os.mkdir(new_dir)
            shutil.move(subject, new_dir+"/"+name)

def cleanup():
    filelist = []
    for root, dirs, files in os.walk(reorg_dir):
        for name in files:
            filelist.append(root+"/"+name)
    directories = [item[0] for item in os.walk(reorg_dir)]
    for dr in directories:
        matches = [item for item in filelist if dr in item]
        if len(matches) == 0:
            try:
                shutil.rmtree(dr)
            except FileNotFoundError:
                pass

if remove_emptyfolders == True:
    cleanup()

ЕСЛИ существует риск нежелательной перезаписи дубликатов файлов

За счет нескольких дополнительных строк мы можем предотвратить перезапись возможных дубликатов. С кодом ниже, дубликаты будут переименованы как:

duplicate_1_filename, duplicate_2_filename 

и т.п.

Сценарий:

#!/usr/bin/env python3

import os
import subprocess
import shutil

# --------------------------------------------------------
reorg_dir = "/path/to/directory_to_reorganize"
exclude = (".jpg") # for example
remove_emptyfolders = True
# ---------------------------------------------------------

for root, dirs, files in os.walk(reorg_dir):
    for name in files:
        subject = root+"/"+name
        if name.startswith("."):
            extension = ".hidden_files"
        elif not "." in name:
            extension = ".without_extension"
        else:
            extension = name[name.rfind("."):]
        if not extension in exclude:
            new_dir = reorg_dir+"/"+extension[1:]
            if not os.path.exists(new_dir):
                os.mkdir(new_dir)
            n = 1; name_orig = name
            while os.path.exists(new_dir+"/"+name):
                name = "duplicate_"+str(n)+"_"+name_orig
                n = n+1
            newfile = new_dir+"/"+name
            shutil.move(subject, newfile)

def cleanup():
    filelist = []
    for root, dirs, files in os.walk(reorg_dir):
        for name in files:
            filelist.append(root+"/"+name)
    directories = [item[0] for item in os.walk(reorg_dir)]
    for dr in directories:
        matches = [item for item in filelist if dr in item]
        if len(matches) == 0:
            try:
                shutil.rmtree(dr)
            except FileNotFoundError:
                pass

if remove_emptyfolders == True:
    cleanup()

РЕДАКТИРОВАТЬ

Имея в виду OP, мы все забыли добавить инструкцию по использованию. Поскольку дублирующие вопросы могут ( и появляются) появляться, тем не менее, это может быть полезно.

Как пользоваться

  1. Скопируйте любой из сценариев в пустой файл, сохраните его как reorganize.py
  2. В разделе заголовка скрипта установите целевой каталог (с файлами для реорганизации):

    reorg_dir = "/path/to/directory_to_reorganize" 

    (используйте кавычки, если каталог содержит пробелы)

    возможные расширения, которые вы хотели бы исключить (вероятно, нет, как показано ниже):

    exclude = ()

    и если вы хотите удалить пустые папки впоследствии:

    remove_emptyfolders = True
  3. Запустите скрипт с помощью команды:

    python3 /path/to/reorganize.py

Примечание: если вы хотите скопировать файлы вместо перемещения , замените:

shutil.move(subject, new_dir+"/"+name)

по:

shutil.copy(subject, new_dir+"/"+name)

Пожалуйста, попробуйте сначала на небольшом образце.

Якоб Влейм
источник
12

Вы можете использовать findс довольно сложной execкомандой:

find . -iname '*?.?*' -type f -exec bash -c 'EXT="${0##*.}"; mkdir -p "$PWD/${EXT}_dir"; cp --target-directory="$PWD/${EXT}_dir" "$0"' {} \;

# '*?.?*' requires at least one character before and after the '.', 
# so that files like .bashrc and blah. are avoided.
# EXT="${0##*.}" - get the extension
# mkdir -p $PWD/${EXT}_dir - make the folder, ignore if it exists

Заменить cpна echoдля пробного запуска.


Более эффективным и аккуратным было бы сохранение bashкоманды в сценарии (скажем, в /path/to/the/script.sh):

#! /bin/bash

for i
do
    EXT="${i##*.}" 
    mkdir -p "$PWD/${EXT}_dir"
    mv --target-directory="$PWD/${EXT}_dir" "$i" 
done

А затем запустите find:

find . -iname '*?.?*' -type f -exec /path/to/the/script.sh {} +

Этот подход довольно гибкий. Например, чтобы использовать имя файла вместо extension ( filename.ext), мы бы использовали это для EXT:

NAME="${i##*/}"
EXT="${NAME%.*}"
Мур
источник
+1; -iname '*.*'следует позаботиться о случаях углового меня обеспокоенные ... хорошей идея!
Rmano
@Rmano не тот *.fig.bakили .profile/.bashrcте, но он должен обрабатывать только файлы с расширениями, по крайней мере. Благодарю.
Муру
6
ls | gawk -F. 'NF>1 {f= $NF "-DIR"; system("mkdir -p " f ";mv " $0 " " f)}'

Расчет списка расширений (после переезда):

ls -d *-DIR

Расчет списка расширений (до переезда):

ls -X | grep -Po '(?<=\.)(\w+)$'| uniq -c | sort -n

(в этом последнем примере мы рассчитываем количество файлов для каждого расширения и сортируем его)


источник
1
извините: опечатка "mkdir -f" была исправлена ​​на "mkdir -p" (игнорировать, если dir уже существует)
Разве uniq не должен применяться после сортировки? И, пожалуйста, не разбирайте вывод ls.
Муру
@muru, (часть 1) ls -X гарантирует, что расширения отсортированы. Окончательная сортировка состояла в том, чтобы просто упорядочить таблицу расширений по количеству появлений - релевантности. (Я прав?).
@muru, (часть 2) ls -X | grep -Po '(?<=\.)(\w+)$'была моей первой идеей, чтобы получить отсортированный список расширений. Это очень плохо? Что ты посоветуешь?
Я забыл, что ls -Xделает. Относительно того, почему я рекомендую против ls, смотрите unix.stackexchange.com/q/128985/70524 и unix.stackexchange.com/q/112125/70524 . Чтобы добиться того, что вы делаете, я бы пошел более длинным путем: find . -type f -name '*?.?*' -print0 | sed -z 's/.*\.//' | sort -zu(с необязательным | uniq -cz, если подсчет необходим). И find ... -print0 | gawk -v RS='\0'(хотя это не очень портативный ) для первого.
Муру
5

Попробуйте этот сценарий оболочки.

#!/bin/sh
src=`dirname "$1"`/`basename "$1"`;
for file in "$src"/*?.?*; do
  if test -f "$file"; then
    dest="$src${file##*.}"_files;
    mkdir -p "$dest";
    mv "$file" "$dest";
  fi;
done;

# pass the directory to re-organize as first argument
# moves only regular files which have extension
# ignores other type of files including
# files having no extension, hidden files, directories, and links.
Прашант Кармакар
источник
1
Прошу прощения, это ошибка. Я должен был заменить каждое вхождение filepathс file. Я исправлю это прямо.
Прашант Кармакар
Пожалуйста, не анализируйте вывод ls. Вместо этого делайfor file in "$src"/*?.?*; do ..
Муру
@muru будет ли это работать правильно, если в имени какого-то файла есть пробелы?
Прашант Кармакар
@PrashantKarmakar да, тогда как readможет иметь неожиданное поведение. Вы также должны заключать в кавычки переменные в командах mkdir и mv.
Муру
Проверьте это, если хотите:for i in *; do printf "%s\n" "$i"; done; for i in $(ls -d); do printf "%s\n" "$i"; done
Муру
2

Если у вас установлен Perl, переименуйте / prename:

rename 's!(.*)\.(\w+)$! mkdir($2); "$2/$&"!ge'  *
Мур
источник