web-dev-qa-db-fra.com

Comment "mv *" sait-il où aller?

j'ai eu une structure de répertoire comme ceci:

ravikumar@ravikumar-RV409-RV509-RV709:~$ tree test
test
|
├── 1
├── 2
├── 3
├── 4
├── a
├── b
└── c

J'ai exécuté la commande mv en tant que:

ravikumar@ravikumar-RV409-RV509-RV709:~$ cd test/
ravikumar@ravikumar-RV409-RV509-RV709:~/test$ mv *

Résultat:

ravikumar@ravikumar-RV409-RV509-RV709:~/test$ tree
.        
|
└── c
    ├── 1
    ├── 2
    ├── 3
    ├── 4
    ├── a
    └── b

Ma question est la suivante: comment mv judge déplace-t-il tous les répertoires et fichiers dans le dernier répertoire? et c'est c.

2
Ravikumar Wagh

Lorsque vous l'avez exécuté dans ce répertoire, mv * était équivalent à mv 1 2 3 4 a b c. Sauf si l'option -t est présente, mv traite le dernier argument non-option comme nom de la destination et est donc déplacé 1, 2, 3 , 4, a et b dans c.

Le shell se développe* dans une liste de noms de fichiers. Lorsque cela se produit, la commande que vous exécutez ne voit pas réellement *, mais uniquement ce à quoi elle a été étendue. La commande que vous exécutez donc ne peut pas indiquer comment traiter ses arguments en fonction de ce que vous avez réellement saisi - elle ne voit que ce que le shell transmet à cela.

Lorsque vous exécutez mv *-- ce qui, comme vous l'avez probablement déduit, devrait presque toujours être évité - le shell et non la commande mv s'agrandit * dans une liste alphabétique de tous les fichiers et répertoires contenus dans le répertoire en cours, à l’exclusion de ceux dont le nom commence par ..

(Vous pouvez personnaliser son fonctionnement via les options de shell dotglob, nullglob et nocaseglob et la variable GLOBIGNORE. Voir la documentation pour plus de détails. .)

Si c n'était pas un répertoire, votre commande échouerait, ne déplacerait rien et afficherait le message d'erreur suivant:

mv: target 'c' is not a directory

Mais comme c était un répertoire, cela fonctionnait tout simplement, même si - si vous exécutiez la commande dans un but autre que celui de voir ce que cela ferait - vous probablement destiné à écrire le nom d'un répertoire distinct à la fin de la commande après le *.

Pour montrer plus intuitivement pourquoi mv n'a aucun moyen de savoir que ses arguments ont été développés à partir de *, supposons que vous ayez écrit:

mv 1 c

Cela déplacerait 1 vers c. Mais peut-être auriez-vous aimé déplacer deux éléments vers c:

mv 1 2 c

Ou cinq éléments:

mv 1 2 3 4 a c

Ou six éléments:

mv 1 2 3 4 a b c

Mais le shell a assuré que mv * passait exactement les mêmes arguments dans le même ordre à la commande mv comme mv 1 2 3 4 a b c.

Il serait souvent gênant de ne pas pouvoir déplacer plusieurs éléments avec un seul appel de mv. Toutefois, même si cela n’était pas autorisé et entraînait toujours une erreur, vous pouvez toujours créer une situation source de confusion analogue dans un répertoire ne contenant que deux entrées. Ensuite, si vous utilisiez mv * dans ce répertoire, vous essaieriez toujours de passer de l'un à l'autre.

Si vous vous demandez ce que le shell va développer, vous pouvez remplacer la commande qui vous intéresse par la commande printf '%s\n', qui affiche tous les arguments qui lui ont été transmis, séparés par des sauts de ligne. Par exemple:

printf '%s\n' *
6
Eliah Kagan

Cette réponse ajoute quelques astuces (mais je pense que la réponse principale est celle de @Eliah Kagan).


J'ai fait deux alias pour rendre les choses un peu plus sûres. Je les stocke dans ~/.bashrc près des autres alias.

alias mv='mv -i'
alias rm='rm -i'

Ces alias rendent les commandes interactives, ils vous demandent si vous voulez écraser/supprimer.

mv vous demandera si vous voulez écraser la cible, si elle existe et est un fichier, ou si la cible est un répertoire et qu'il existe déjà un fichier portant le même nom. Cela vous aidera à éviter les erreurs, par exemple lorsque vous utilisez des caractères génériques, mv *.


S'il vous plaît noter

  • Ces alias ne fonctionnent qu'avec votre propre ID utilisateur. Ils ne fonctionnent pas avec Sudo. Si vous voulez que les alias soient développés après Sudo, vous devez le configurer séparément . Si vous souhaitez qu'un alias particulier fonctionne dans un shell racine (comme vous l'obtenir de Sudo -s ou Sudo -i), vous devez créer un alias similaire et le stocker dans /root/.bashrc.

  • Vous pouvez remplacer les alias (et obtenir le comportement original de mv et rm) avec le préfixe 'backslash`,

    \rm filename
    \mv filename1 filename2
    
  • Vous pouvez vous habituer au drapeau -i passé automatiquement et avoir un faux sentiment de sécurité , par exemple lors de l'exécution d'un autre système, où ces alias ne sont pas actifs.

1
sudodus