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.
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. mv "$1"/* "$1/.."
Ceci est plus ou moins basé sur réponse du dessert .
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ésUne 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
é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
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).
$ 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
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 sh
pour 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 sh
name__, bash
name__, csh
name__, 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 sed
ou 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 sh
pour 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 awk
pour cette édition, vous pouvez le faire avec sed
ou pure find
name__
find . -type d -name source | sed 's|\(.*\)|mv \1/* \1/..|' # sed
find . -type d -name source -exec echo 'mv {}/* {}/..' ';' # find/echo
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 find
renvoie 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 sed
supprime source
du résultat de la commande find
name__.
Et le reste est juste une itération (for
name__) et une commande de déplacement (mv
name__)
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$