web-dev-qa-db-fra.com

for boucle dans les dossiers avec \ n caractère dans leurs noms

J'ai quelques dossiers avec \n caractère leurs noms.

par exemple:

$ ls
''$'\n''Test'

Cela fait référence à un dossier avec le nom du test et une ligne vide avant son nom.

Alors quand j’exécute des scripts comme celui-ci, dans son répertoire parent:

while IFS= read -r d; do 
    rmdir $d
done < <(find * -type d)

Ça montre:

rmdir: failed to remove '': No such file or directory
rmdir: failed to remove 'Test': No such file or directory

Parce qu'il s'exécute deux fois, une fois sur \n et l'autre sur Test, car le nom du dossier a deux lignes.

Alors, comment puis-je résoudre ce problème de telle sorte que, le script sache que \nTest n'est qu'un dossier?

9
Tara S Volpe

Vous n'avez qu'une seule commande à cet endroit. Il est donc suffisant d'appeler find avec -exec flag appelant rmdir:

find -depth -type d -exec rmdir {} \;

Ou utilisez l'option -delete comme dans find -type d -delete, mais cela ne fonctionnera pas avec des répertoires non vides. Pour cela, vous aurez également besoin du drapeau -empty. Notez également que -delete implique -depth pour pouvoir être ignoré. Ainsi, une autre alternative viable qui conserve tout en un processus:

find -type d -empty -delete

Si le répertoire n'est pas vide, utilisez rm -rf {} \;. Pour isoler uniquement les répertoires avec \n dans le nom de fichier, nous pouvons combiner les commandes --- de bash citation ANSI-C$'...' avec -name:

find  -type d -name $'*\n*' -empty -delete

POSIX-ly, nous pourrions le gérer de cette façon:

find -depth  -type d -name "$(printf '*\n*' )" -exec rmdir {} \;

Il est à noter que si votre objectif est de supprimer des répertoires, alors -delete est suffisant, cependant si vous voulez exécuter une commande sur directory, alors -exec est le plus approprié.

Voir également

12

Vous pouvez utiliser des globes de shell au lieu de find:

for d in */ ; do 
    rmdir "$d"
done

Shell glob */ correspond à tous les dossiers du répertoire en cours. Cette construction en boucle prend en charge le fractionnement correct de Word automatiquement.

Notez que selon les options de votre shell, cela peut ignorer les dossiers cachés (nom commençant par un.). Ce comportement peut être modifié pour correspondre à tous les fichiers de la session en cours à l’aide de la commande shopt -s dotglob.

N'oubliez pas de toujours citer vos variables.

10
Byte Commander

Les deux réponses écrites jusqu’à présent appellent rmdirune fois par répertoire, mais comme rmdirpeut prendre plusieurs arguments, je me demande: n’y at-il pas un moyen plus efficace?

On pourrait simplement faire

rmdir */

et c’est certainement le moyen le plus simple et le plus efficace, mais cela peut générer une erreur dans le cas de de nombreux répertoires (voir Quel est le maximum longueur des arguments en ligne de commande dans gnome-terminal? ). Si vous souhaitez que cette approche fonctionne de manière récursive, activez l'option Shell globstaravec shopt -s globstar et utilisez **/*/ au lieu de */.

Avec GNU find(et si nous ne voulons pas simplement utiliser -delete), nous pourrions faire

find -depth -type d -exec rmdir {} +

ce qui construit la ligne de commande "de la même manière que xargsconstruit ses lignes de commande" (man find). Remplacez -depth par -maxdepth 1 si vous ne voulez pas que cela fonctionne de manière récursive.

Un troisième et brillant moyen IMO est expliqué par steeldriver dans cette réponse :

printf '%s\0' */ | xargs -0 rmdir

Ceci utilise le shell intégré printfpour construire une liste d'arguments délimitée par des zéros. Cette liste est ensuite redirigée vers xargsqui appelle rmdiraussi souvent que nécessaire. Vous pouvez le faire fonctionner de manière récursive avec shopt -s globstar et **/*/ au lieu de */ comme ci-dessus.

6
dessert