web-dev-qa-db-fra.com

Comment organiser les fichiers en fonction de la première lettre de leur nom de fichier dans des dossiers AZ

Je recherche un moyen (de préférence terminal) d’organiser plus de 1000 polices par leur première lettre.

Fondamentalement, créez les répertoires A-Z, #, puis déplacez les fichiers de police dans ces répertoires en fonction du premier caractère de leur nom de fichier. Les polices commençant par les chiffres [0-9] ou d’autres caractères spéciaux doivent être déplacées vers le répertoire #.

15
Parto

Une option python tardive:

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

Comment utiliser

  1. Copiez le script dans un fichier vide, enregistrez-le sous move_files.py
  2. Exécutez-le avec le répertoire comme argument:

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

Le script créera uniquement le (sous) répertoire (-ies) (majuscule) s'il est réellement nécessaire

Explication

Le scénario:

  • liste les fichiers, obtient le premier caractère (définit le chemin source):

    for f in os.listdir(dr):
        s = f[0]; fsrc = path(dr, f)
    
  • vérifie si l'élément est un fichier:

    if os.path.isfile(fsrc):
    
  • définit le dossier ciblé si le premier caractère est alpha ou non:

    target = path(dr, s.upper()) if s.isalpha() else path(dr, "#")
    
  • vérifie si le dossier existe déjà ou non, le crée sinon:

    if not os.path.exists(target):
        os.mkdir(target)
    
  • déplace l'élément dans le dossier correspondant:

    shutil.move(fsrc, path(target, f))
    
13
Jacob Vlijm

Code-golfé mais lisible avec seulement deux commandes et deux expressions régulières:

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

Si vous avez une quantité énorme de fichiers à déplacer, trop pour pouvoir être insérés dans la liste des arguments du processus (oui, il y a une limite et cela peut ne représenter que quelques kilo-octets), vous pouvez générer la liste de fichiers avec une commande différente et la diriger vers prename, e. g.:

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

Cela présente l’avantage supplémentaire de ne pas essayer de déplacer le nom de fichier littéral [[:alnum:]]?* si aucun fichier ne correspond au modèle glob. find autorise également beaucoup plus de critères de correspondance que l'écriture de shell. Une autre solution consiste à définir l'option Shell à nullglob et à fermer le flux d'entrée standard de prename.1

Dans les deux cas, supprimez le commutateur -n pour déplacer réellement les fichiers et ne montrez pas simplement comment ils seraient déplacés.

Addendum: Vous pouvez à nouveau supprimer les répertoires vides avec:

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

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

11
David Foerster

Je n'ai pas trouvé de solution intéressante pour mettre les noms de répertoire en majuscule (ou déplacer les fichiers avec des lettres en majuscule), bien que vous puissiez le faire ultérieurement avec 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

ou plus lisiblement:

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

Supprimer echo après le test pour réellement déplacer les fichiers

Et alors

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

supprimez -n s'il semble bon après le test et exécutez-le à nouveau.

8
Zanna

Si zsh, une fonction et quelques commandes zmvvous intéressent:

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'

La fonction mmvcrée le répertoire et déplace le fichier. zmvfournit ensuite une correspondance de modèle et une substitution. Tout d’abord, déplacez les noms de fichiers en commençant par un alphabet, puis tout le reste:

$ 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

Réexécutez l'opération sans la définition de echodans mmvname __ afin d'effectuer le déplacement.

8
muru

Les commandes suivantes du répertoire contenant les polices doivent fonctionner. Si vous souhaitez utiliser de l’extérieur du répertoire de stockage des polices, remplacez for f in ./* par for f in /directory/containing/fonts/*. C'est une méthode très basée sur Shell, donc assez lente et non récursive. Cela ne créera que des répertoires, s'il existe des fichiers qui commencent par le caractère correspondant.

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

En une ligne, toujours à partir du répertoire de stockage des polices:

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

Une méthode utilisant find, avec une manipulation de chaîne similaire, utilisant le développement de paramètres bash, qui sera récursive et devrait être un peu plus rapide que la version pure Shell:

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' \;

Ou plus lisiblement:

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' \;
7
Arronical

Mappez chaque nom de fichier sur un nom de répertoire en utilisant tr, puis mkdir et 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'
5
xn.