web-dev-qa-db-fra.com

Comment déplacer des fichiers de sous-répertoires ayant le même nom de répertoire dans son répertoire supérieur / parent relatif?

Donc, j'ai une structure de répertoire comme celle-ci:

parent/
├── sub1
│   └── source
│       └── file1
│       └── file2
├── sub2
│   └── sub2.1
│       └── source
│           └── something1
│           └── something2
└── sub3
    └── sub3.1
        └── sub3.1.1
            └── source
                └── other.Zip

Je souhaite déplacer tous les fichiers (avec un nom de fichier différent) à partir de tous les répertoires nommés source vers son répertoire supérieur/parent relatif. Donc, le résultat devrait ressembler à ceci:

parent/
├── sub1
│   ├── file1
│   ├── file2
│   └── source
├── sub2
│   └── sub2.1
│       ├── something1
│       ├── something2
│       └── source
└── sub3
    └── sub3.1
        └── sub3.1.1
            ├── other.Zip
            └── source

Existe-t-il un moyen simple ( une doublure ) d’effectuer cette opération, éventuellement à l’aide de la commande find? De préférence, ce n'est pas trop complexe pour que je comprenne. : D Je suis assez nouveau pour Linux.

EDIT: Je vais aussi créer un script bash (pour pouvoir l'utiliser facilement) hors de la solution. Par exemple: ./movefiles.sh myfolder Donc, de préférence, la solution peut facilement contenir des variables umm?, En particulier celles qui comportent des symboles tels que . (s'il s'agit d'un répertoire caché), #, @, etc.

5
Lukman Hakim

Si vous voulez une solution find, vous pouvez utiliser ceci:

find parent -name "source" -type d -exec bash -c 'cd "$1"; mv * ..' bash {} \;

Explication:

  • find parent -name "source" -type d - Pour chaque répertoire nommé source dans parent...
  • -exec bash -c '...' bash {} \; - Appelez Bash avec bash en tant que $0 et le chemin du répertoire en tant que $1
  • cd "$1"; mv * .. - cd dans le répertoire; déplace tout son contenu d'un niveau.
    • Alternative: mv "$1"/* "$1/.."

Ceci est plus ou moins basé sur réponse du dessert .

Y compris les fichiers cachés

find parent -name "source" -type d -exec bash -c 'shopt -s dotglob; cd "$1"; mv * ..' bash {} \;
  • shopt -s dotglob - Permet aux globs d'inclure des fichiers cachés
4
wjandrea

Une simple boucle for avec l'option globstar activée (exécutez shopt -s globstar pour le faire) fera l'affaire:

dirname="source"
for i in ./**/"$dirname"/; do
  mv "$i"* "${i%$dirname/}"
done

Le GNU parallel Install parallel équivalent pour cette boucle est:

parallel mv {}* {//}/ ::: ./**/"$dirname"/

Une approche totalement différente consiste à utiliser rename pour supprimer la partie “$ dirname /” de la chaîne:

rename "s/$dirname\///" **/"$dirname"/*

Quotation avec des guillemets, comme je l’ai fait ici, conserve la signification de chaque caractère spécial sauf $, `, \ et !. Si vous souhaitez inclure des fichiers de points "cachés" dans l’une des solutions ci-dessus, définissez l’option dotglob avant de l’exécuter: shopt -s dotglob

Explication

globstar
Si défini, le modèle ** utilisé dans un contexte de développement de chemin correspond à tous les fichiers et à aucun ou plusieurs répertoires et sous-répertoires. Si le modèle est suivi d'un /, seuls les répertoires et les sous-répertoires correspondent.

./**/source/ correspond à chaque répertoire nommé source dans et sous le répertoire en cours, la commande mv déplace chaque fichier du répertoire vers son répertoire parent - ${i%source/} est une extension de paramètre qui supprime la chaîne source/ de la fin de la chaîne (chemin).

Exemple d'exécution

$ tree
.
├── sub1
│   └── source
│       ├── file1
│       └── file2
├── sub2
│   └── sub2.1
│       └── source
│           ├── something1
│           └── something2
└── sub3
    └── sub3.1
        └── sub3.1.1
            └── source
                └── other.Zip

$ dirname="source"
$ for i in ./**/"$dirname"/; do mv "$i"* "${i%$dirname/}"; done
$ tree
.
├── sub1
│   ├── file1
│   ├── file2
│   └── source
├── sub2
│   └── sub2.1
│       ├── something1
│       ├── something2
│       └── source
└── sub3
    └── sub3.1
        └── sub3.1.1
            ├── other.Zip
            └── source
8
dessert

Pour ce genre de tâches où il peut y avoir des surprises, l'un des meilleurs moyens est "script-a-script". Nous exécutons une commande qui génère un script, généralement très répétitif, pour effectuer la tâche. Une fois que nous sommes convaincus que c'est correct, nous le lançons via shpour l'exécuter. Cela transforme un problème complexe en un problème de montage beaucoup plus simple, et il s’agit d’un technique extrêmement générale, applicable à toutes sortes de problèmes, et pas seulement à ce problème fastidieux de transfert de fichiers vers le haut. Il a l'avantage de ne pas utiliser de constructions de shell exotiques et fonctionne donc dans chaque shell (Posix pur shname__, bashname__, cshname__, etc.). Parce que vous voyez toutes les commandes de base avant qu'elles ne soient exécutées, c'est un bon exemple de "look-before-you-bisse".

Commencez par trouver tous les répertoires que nous allons modifier:

$ find . -type d -name source

Cela donne

./sub3/sub3.1/sub3.1.1/source
./sub2/sub2.1/source
./sub1/source

Ensuite, pour chacun de ces répertoires que nous voulons déplacer le contenu d'un répertoire à un autre, nous pensons à la commande que nous aurions exécutée:

$ mv $dir/* $dir/..

Nous utilisons awk(ou sedou autre) pour écrire les commandes. Nous dirigeons donc la liste des répertoires dans awk:

$ find . -type d -name source \
  | awk '{printf("mv %s/* %s/..\n", $0, $0);}'
mv ./sub3/sub3.1/sub3.1.1/source/* ./sub3/sub3.1/sub3.1.1/source/..
mv ./sub2/sub2.1/source/* ./sub2/sub2.1/source/..
mv ./sub1/source/* ./sub1/source/..

Nous pouvons faire cela autant de fois que nécessaire, en éditant avec soin, jusqu'à ce que les commandes soient correctes.

Ensuite, nous transmettons tout cela par shpour le faire:

$ find . -type d -name source \
  | awk '{printf("mv %s/* %s/..\n", $0, $0);}' \
  | sh

Il est souvent bon que la sortie finale dise ce qu’elle fait et quitte à la première erreur, utilisez donc sh -e -x:

$ find . -type d -name source  \
  | awk '{printf("mv %s/* %s/..\n", $0, $0);}' \
  | sh -e -x

Si vous n'êtes pas sûr de awkpour cette édition, vous pouvez le faire avec sedou pure findname__

find . -type d -name source | sed 's|\(.*\)|mv \1/* \1/..|'  # sed
find . -type d -name source -exec echo 'mv {}/* {}/..' ';'   # find/echo
2
jonathanjo

Vous pouvez utiliser le code ci-dessous pour obtenir ce que vous voulez:

dir=$(find . -name 'source' | sed s:source::)
for path in $dir; do
    mv "$path"source/* "$path"
done

La commande findrenvoie le chemin du répertoire du répertoire parent au répertoire source. Dans ce find . -name 'source' '.' représente le répertoire parent et 'source' représente le sous-répertoire que vous souhaitez rechercher.

La commande sedsupprime sourcedu résultat de la commande findname__.

Et le reste est juste une itération (forname__) et une commande de déplacement (mvname__)

2
Sathish Kanna

Un simple

dir='source'
for i in `find . -name  "*" | grep $dir/*| grep -v "$dir$"`; do
mv "$i" "${i%$dir/*}"
done

Tester

$ cd Desktop/test/
/Desktop/test$ tree
.
├── sub1
│   └── source
│       ├── file1
│       └── file2
├── sub2
│   └── sub2.1
│       └── source
│           ├── something1
│           └── something2
└── sub3
    └── sub3.1
        └── sub3.1.1
            └── source
                └── other.Zip

9 directories, 5 files
/Desktop/test$ dir='source'
/Desktop/test$ for i in `find . -name  "*" | grep $dir/*| grep -v "$dir$"`; do
>    mv "$i" "${i%$dir/*}"
> done
/Desktop/test$ tree
.
├── sub1
│   ├── file1
│   ├── file2
│   └── source
├── sub2
│   └── sub2.1
│       ├── something1
│       ├── something2
│       └── source
└── sub3
    └── sub3.1
        └── sub3.1.1
            ├── other.Zip
            └── source

9 directories, 5 files
/Desktop/test$ 
0
Vijay