Comment remplacer un dossier dont le nom est une date, c'est-à-dire AAAAMMJJ avec la hiérarchie des dossiers de l'année, du mois, de la date?

8

J'ai une liste de dossiers qui ont des dates pour les noms. Les dates sont au format AAAAMMJJ (par exemple 20150129). Dans ces dossiers se trouvent des documents texte liés à cette date spécifique.

Je voudrais les restructurer dans une hiérarchie de dossiers allant d'année en mois et jusqu'à ce jour, et déplacer les documents texte dans le dossier «date» correspondant plus bas dans la hiérarchie.

En d'autres termes, je voudrais que le dossier «racine» soit nommé d'après l'année comme 2015, puis créer des sous-dossiers nommés avec des mois comme 01, puis créer d'autres sous-dossiers nommés avec des dates comme 29 qui contiennent les documents texte correspondants .

Le chemin ressemblerait donc à 2015/01/29/file.txtou 2015>01>29>file.txt.

J'ai jeté un coup d'œil à Automator et il semble que quelque chose comme ça ne soit pas possible bien que je puisse me tromper, donc je voudrais savoir ...

  1. Existe-t-il une solution simple à ce problème que tout profane peut comprendre, par exemple un workflow Automator, ou cela nécessite-t-il une certaine compréhension des commandes de terminal et des expressions régulières?

  2. Comment pourrait-on résoudre ce problème à condition qu'il existe en fait une solution?

davidjnatarajan
la source
À qui a voté pour clore cette question comme "trop ​​large", pourquoi? Je suis curieux de savoir ce qui est "trop ​​large" sur cette question?
user3439894
Ces dossiers YYYYMMDD sont-ils tous directement dans un dossier maître ou sont-ils répartis dans une hiérarchie plus large?
nohillside
@patrix Dans mon cas, ils sont tous dans le même répertoire ou dossier principal
davidjnatarajan

Réponses:

8

En supposant que tous ces dossiers YYYYMMDD font partie du même répertoire parent que vous pourriez exécuter

cd PARENT_DIRECTORY
for d in */; do
    [[ $d =~ [0-9]{8}/ ]] || continue
    mkdir -p -- "${d:0:4}/${d:4:2}"
    mv -- "$d" "${d:0:4}/${d:4:2}/${d:6:2}"
done
  • La for d in */; doboucle lit toutes les entrées du répertoire, la fin /garantit que seuls les noms de répertoire correspondent réellement
  • [[ $d =~ [0-9]{8}/ ]] teste si l'entrée actuelle se compose de 8 chiffres et continue avec l'entrée suivante sinon
  • ${d:0:4}/${d:4:2}/${d:6:2}utilise l'expansion des paramètres à l'intérieur bashpour créer une chaîne contenant le nouveau chemin
  • Le --dans les deux mkdiret mvempêche le problème au cas où le répertoire ou le nom de fichier commence par un -. Cela ne peut pas arriver ici, mais c'est probablement une bonne pratique de toute façon.

Merci à @terdon et @ user3439894 pour des idées sur la façon d'améliorer le script original.

nohillside
la source
Merci pour la réponse, cela fonctionne parfaitement! Je pense que cette solution est meilleure que celle fournie par @grgarside car elle est beaucoup plus rapide, surtout lorsqu'il s'agit d'un corpus massif comprenant des milliers de documents texte.
davidjnatarajan
8

Vous pouvez utiliser ce qui suit dans Terminal. cddans le dossier contenant, puis exécutez ce qui suit:

find . -type f -exec bash -c \
  'F=$(sed -E "s#^\./([0-9]{4})([0-9]{2})([0-9]{2})#\1/\2/\3#" <<< $1);\
  mkdir -p -- $(dirname "$F");\
  mv -- "$1" "$F"' - {} \;

find . -type fobtient chaque fichier du répertoire courant de manière récursive.
-exec bash -couvre un shell pour exécuter les commandes suivantes.
F=$(…)ouvre un sous-shell et utilise sed sur le chemin du fichier pour manipuler le chemin dans les dossiers.
^\./([0-9]{4})([0-9]{2})([0-9]{2})est une expression régulière avec trois groupes de capture, comme suit: est un remplacement, où chaque groupe de capture ( , etc.) est séparé par . crée les répertoires dans lesquels déplacer les fichiers. déplace chaque fichier dans son dossier correspondant.
\1/\2/\3\1/
mkdir -p -- $(dirname "$F")
mv -- "$1" "$F"

Cela prend la hiérarchie à gauche et la convertit en hiérarchie à droite:

├── 20170201               └── 2017
   └── abcdefghij             ├── 02
└── 20170302                      └── 01
    └── abcdefghij 2                  └── abcdefghij
                               └── 03
                                   └── 02
                                       └── abcdefghij 2

S'il y a d'autres fichiers dans le dossier contenant avec une date comme nom, ils seront déplacés comme s'ils étaient un dossier. Pour éviter cela, remplacez la deuxième ligne par:

  'F=$(sed -E "s#^\./([0-9]{4})([0-9]{2})([0-9]{2})(?:/.+)#\1/\2/\3#" <<< $1);\

Le (?:/.+)garantit que le chemin a un composant ultérieur, ignorant ainsi tout ce qui ne contient pas d'enfant dans le répertoire parent qui sont des fichiers.

grg
la source
@klanomath regex101.com
grg
@grgarside Thanx
klanomath