Grouper des fichiers dans certains dossiers

12

J'ai quelques fichiers avec des extensions différentes telles que *.pdf, *.mp3, *.jpget quelques autres. Tous sont stockés dans un parentrépertoire.

Comment puis-je obtenir une liste de toutes les extensions, créer des dossiers basés sur ces extensions, puis déplacer tous les fichiers dans leurs dossiers pertinents?

αғsнιη
la source

Réponses:

13

Le script python ci-dessous fait le travail. Les fichiers cachés sont stockés séparément dans un dossier, ainsi que les fichiers sans extension.

Puisqu'il pourrait être utilisé à des fins plus diverses, j'ai ajouté quelques options:

  • Vous pouvez définir les extensions que vous souhaitez exclure de la "réorganisation". Si vous voulez simplement tout déplacer, réglezexclude = ()
  • Vous pouvez choisir quoi faire avec les dossiers vides ( remove_emptyfolders = Trueou False)
  • Dans le cas où vous souhaitez copier les fichiers au lieu de les déplacer , remplacez la ligne:
shutil.move(subject, new_dir+"/"+name)

par:

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

Le script:

#!/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()

EN CAS de risque d'écrasement indésirable de fichiers en double

Au détriment de quelques lignes supplémentaires, nous pouvons éviter d'écraser d'éventuels doublons. Avec le code ci-dessous, les doublons seront renommés comme:

duplicate_1_filename, duplicate_2_filename 

etc.

Le script:

#!/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()

ÉDITER

Avec OP à l'esprit, nous avons tous oublié d'ajouter une instruction sur la façon d'utiliser. Étant donné que des questions en double peuvent apparaître ( et apparaissent), cela peut néanmoins être utile.

Comment utiliser

  1. Copiez l'un des scripts dans un fichier vide, enregistrez-le sous reorganize.py
  2. Dans la section head du script, définissez le répertoire ciblé (avec les fichiers à réorganiser):

    reorg_dir = "/path/to/directory_to_reorganize" 

    (utilisez des guillemets si le répertoire contient des espaces)

    extensions possibles que vous souhaitez exclure (probablement aucune, comme ci-dessous):

    exclude = ()

    et si vous souhaitez supprimer les dossiers vides par la suite:

    remove_emptyfolders = True
  3. Exécutez le script avec la commande:

    python3 /path/to/reorganize.py

NB si vous souhaitez copier les fichiers au lieu de les déplacer , remplacez:

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

par:

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

Veuillez d'abord essayer sur un petit échantillon.

Jacob Vlijm
la source
12

Vous pouvez utiliser findavec une execcommande quelque peu complexe :

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

Remplacez cppar echopour un essai à sec.


Plus efficace et plus ordonné serait de sauvegarder la bashcommande dans un script (disons, à /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

Et puis exécutez find:

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

Cette approche est assez flexible. Par exemple, pour utiliser le nom de fichier au lieu de l'extension ( filename.ext), nous utiliserions ceci pour EXT:

NAME="${i##*/}"
EXT="${NAME%.*}"
muru
la source
+1; ils -iname '*.*'devraient s'occuper des caisses d'angle qui m'inquiétaient ... bonne idée!
Rmano
@Rmano pas les *.fig.bakou .profile/.bashrc, mais il ne devrait au moins gérer que les fichiers avec des extensions. Merci.
muru
6
ls | gawk -F. 'NF>1 {f= $NF "-DIR"; system("mkdir -p " f ";mv " $0 " " f)}'

Calcul de la liste des extensions (après le déplacement):

ls -d *-DIR

Calcul de la liste des extensions (avant de déménager):

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

(dans ce dernier exemple, nous calculons le nombre de fichiers pour chaque extension et le trions)


la source
1
désolé: une faute de frappe "mkdir -f" a été corrigée en "mkdir -p" (pour ignorer si dir existe déjà)
L'uniq n'est-il pas censé être appliqué après le tri? Et veuillez ne pas analyser la sortie de ls.
muru
@muru, (partie 1) ls -X garantit que les extensions sont triées. Le tri final consistait simplement à classer le tableau des extensions par nombre d'occurrences - pertinence. (Je me trompe?).
@muru, (partie 2) a ls -X | grep -Po '(?<=\.)(\w+)$'été ma première idée d'obtenir la liste triée des extensions. C'est très mauvais? Que suggérez-vous?
J'ai oublié ce qui se ls -Xpasse. Pour savoir pourquoi je déconseille ls, voir unix.stackexchange.com/q/128985/70524 et unix.stackexchange.com/q/112125/70524 . Pour réaliser ce que vous faites, j'irais sur un chemin plus long: find . -type f -name '*?.?*' -print0 | sed -z 's/.*\.//' | sort -zu(avec une option | uniq -cz, si les comptes sont nécessaires). Et find ... -print0 | gawk -v RS='\0'(même si ce n'est pas très portable ) pour le premier.
muru
5

Essayez ce script shell.

#!/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.
Prashant Karmakar
la source
1
Je suis désolé, c'est une erreur. Je devrais avoir à remplacer chaque occurrence de filepathpar file. Je vais corriger cela directement.
Prashant Karmakar
Veuillez ne pas analyser la sortie de ls. Au lieu de cela, faitesfor file in "$src"/*?.?*; do ..
muru
@muru cela fonctionnera-t-il correctement si le nom d'un fichier a des espaces?
Prashant Karmakar
@PrashantKarmakar oui, alors que readpeut avoir un comportement inattendu. Vous devez également citer les variables dans les commandes mkdir et mv.
muru
Testez-le, si vous voulez:for i in *; do printf "%s\n" "$i"; done; for i in $(ls -d); do printf "%s\n" "$i"; done
muru
2

Si vous avez renommé / prénommé Perl:

rename 's!(.*)\.(\w+)$! mkdir($2); "$2/$&"!ge'  *
muru
la source