web-dev-qa-db-fra.com

Déplacer les fichiers nommés dans des répertoires auto-nommés

J'ai plusieurs milliers de fichiers dans un répertoire que je voudrais rassembler dans des répertoires comme ceci:

À partir de ceci:

└── Files
    ├── AAA.mkv
    ├── AAA.nfo
    ├── AAA-picture.jpg
    ├── BBB.mp4
    ├── BBB.srt
    ├── BBB-clip.mp4
    ├── CCC.avi
    ├── CCC.srt
    ├── CCC-clip.mov
    └── CCC.nfo

Pour ça:

└── 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

Les noms de fichiers varient en longueur et en nombre de mots, parfois séparés par des espaces et éventuellement par quelques traits d'union (en plus de ceux se terminant par '-short'. Il s'agit principalement de fichiers vidéo avec une variété de formats/conteneurs: mov/mpg/mkv/mp4/avi/ogg. Certains sont sous-titrés. Certains ont des fichiers avec des métadonnées associées (.nfo ou -clip).

Edit: Les fichiers principaux sont des vidéos (c’est là que je voudrais dessiner le nom du répertoire). Les fichiers associés représentent des métadonnées. Certains sont différents en nommant seulement par l'extension. Il y a une demi-douzaine d'autres variantes du nom de fichier de base, comme -clip.mp4 -clip.mov ou -picture.jpg. J'ai pensé que si quelque chose était suggéré avec ces quelques-uns, je pourrais (espérons-le) travailler sur le reste. En résumé, AAA.mkv se déplace dans un répertoire appelé AAA. Ensuite, tous les fichiers de métadonnées commençant par AAA le rejoignent (dans cet exemple: AAA-picture.jpg et AAA.nfo). Donc, le nom de base est en fait une sous-chaîne dans le cas du fichier AAA-picture.jpg. Je dirais qu'il est probablement relativement sûr d'utiliser simplement le trait d'union comme facteur de délimitation ... bien que "-clip" ou "-picture" dans son ensemble soit plus sûr.

Comment puis-je faire cela sans avoir le syndrome du canal carpien? J'ai regardé this mais c'était suffisamment différent pour que mes faibles capacités de script s'éteignent.

Je vous remercie.

4
MrFinn

Bien que votre question porte la mention bash, il serait quelque peu gênant (à mon humble avis) d’utiliser bash pour une telle tâche. Je suggérerais d'utiliser python car il comporte de nombreuses fonctions utiles pour les tâches complexes et que cette réponse fournit une solution utilisant ce langage.

Ce qui se passe essentiellement ici est que nous utilisons regex pour scinder les noms de fichiers en plusieurs délimiteurs, obtenir uniquement la première partie et utiliser un ensemble unique de ces premières parties comme noms de base pour les nouveaux répertoires.

Nous parcourons ensuite à nouveau le répertoire principal et trions les fichiers aux emplacements appropriés.

Le script ne fait rien de spectaculaire, et en analyse algorithmique, cela ne ferait pas très bien, à cause des boucles for imbriquées, mais pour une solution "rapide, sale, mais réalisable", tout va bien. Si vous êtes intéressé par ce que chaque ligne fait, il y a beaucoup de commentaires ajoutés pour expliquer la fonctionnalité

Remarque , la démonstration présente uniquement l'impression des nouveaux noms de fichiers à des fins de test uniquement. Décommentez la partie os.rename() pour déplacer le fichier.

La démo

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

Script lui-même

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

Notes complémentaires:

  • Le script peut être adapté pour séparer des fichiers en plusieurs séparateurs (dans la fonction re.split()): ajoutez des crochets (signifiant "[.-]"), ajoutez les caractères de votre choix.
  • La partie mobile est exécutée avec la fonction os.rename(). Vous pouvez également utiliser import shutil et utiliser la fonction shutil.move(). Voir https://stackoverflow.com/a/8858026/3701431
5

J'ai fait un petit script bash pour le faire, simplifié et amélioré grâce aux commentaires de OP, @dannysauer, @Arronical et @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

Exécutez d’abord avec echo puis supprimez echo pour déplacer les fichiers. Le script doit être exécuté à partir du répertoire dans lequel vous souhaitez déplacer les fichiers. Si vous préférez, la commande est une ligne:

for file in *; do mkdir -p "${file%%[.-]*}"; if [[ -d "${file%%[.-]*}" ]]; then if [[ -f "$file" ]]; then echo mv -v -- "$file" "${file%%[.-]*}"; fi ; fi ; done

(encore une fois, supprimez echo après le test)

Explication:

  • for file in *; do mkdir -p "${file%%[.-]*}" crée un répertoire avec le nom de la première partie du nom de chaque fichier (jusqu'au premier trait d'union ou point) Le drapeau -p est très important ici - sans cela, le script ne bougera que le premier fichier correspondant (merci à Arronical pour signalant que -p arrêtera mkdir d'essayer de créer des répertoires existants et de s'en plaindre )
  • 2>/dev/null le script se plaint de ne pas pouvoir créer un répertoire du même nom que lui-même (mais qu'il fonctionne toujours). Nous éliminons donc l'erreur. Cette opération n'est pas nécessaire si vous utilisez une ligne.
  • if [[ -d "${file%%[.-]*}" ]]; then s'il existe un répertoire portant ce nom (si la mkdir a réussi), alors ...
  • if [[ -f "$file" ]] si nous traitons avec un fichier (pas un répertoire ou autre chose), alors ...
  • mv -v -- "$file" "${file%%[.-]*}" déplacez-le dans le répertoire correspondant.
9
Zanna

Sur un petit script 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))

Pour l'utiliser:

  • copiez-le dans un fichier vide
  • Enregistrez-le sous move_into.py
  • Exécutez-le avec le répertoire comme argument:

    python3 /path/to/move_into.py /path/to/directory
    

Le script suppose que tous les fichiers (pertinents) ont des extensions. Si un fichier n'a pas d'extension, il ne se passe rien. Si c'est un problème, veuillez le mentionner, peut être changé facilement.

Explication

  • Le script recherche une extension possible.
  • S'il n'est pas présent, le script laisse le fichier (ou le répertoire) seul.
  • Sinon, le fichier sera divisé par "-", le cas échéant, la première section est ensuite utilisée pour créer des dossiers (si nécessaire).
  • Sinon, le nom de base du fichier est utilisé pour nommer le dossier.

Par la suite, le fichier est déplacé dans le dossier correspondant.

5
Jacob Vlijm