J'ai accidentellement oublié de spécifier la destination avant d'appuyer sur la touche Retour. Où, mv ./*sans spécifier de destination, déplace-t-il les fichiers et les répertoires du répertoire actuel?
"Je suis surpris que mvj'accepte un argument" Ce n'est pas le cas. Il accepte tous les arguments que le shell lui a transmis après avoir développé le *.
glglgl
Réponses:
41
Si le dernier argument était un répertoire, vous venez de déplacer tous les fichiers et répertoires de votre répertoire de travail actuel (sauf ceux dont les noms commencent par des points) dans ce répertoire. S'il y avait deux fichiers, le premier fichier peut avoir écrasé le deuxième fichier.
Voici quelques démonstrations:
Plus de deux fichiers et le dernier argument est un fichier
$ mkdir d1 d2 d3
$ touch a b c e
$ mv *
mv: target 'e' is not a directory
Plus de deux fichiers et le dernier argument est un répertoire
$ mkdir d1 d2 d3
$ touch a b c
$ mv -v *
'a' -> 'd3/a'
'b' -> 'd3/b'
'c' -> 'd3/c'
'd1' -> 'd3/d1'
'd2' -> 'd3/d2'
Deux fichiers
$ touch a b
$ mv -v *
'a' -> 'b'
Plus d'explications
Le shell développe le glob ( *) en arguments pour mv. Le glob est généralement développé par ordre alphabétique. mvvoit toujours une liste de fichiers et de répertoires. Il ne voit jamais le globe lui-même.
La commande mvprend en charge deux types de déplacement. L'un est mv file ... directory. L'autre est mv old-file-name new-file-name(ou mv old-file-name directory/new-file-name).
Je vais d'abord faire une base de test - 5 fichiers et un dossier:
touch file1 file2 file3 file4 file5
mkdir folder
Ensuite, je vais exécuter une commande de test. L' -voption spécifie que je veux que chaque commande exécutée par le shell soit imprimée stderr. L' -xoption spécifie que je veux que le même soit imprimé stderr- mais je veux que ce soit fait après que la commande soit évaluée mais avant que le shell ne l'exécute.
Vous voyez donc que la commande que j'alimente le shell est echo mv *et que la commande que le shell exécute après avoir* été développée est echo mvsuivie par tous ces fichiers et le dossier.
Ceci est le résultat de la set [+-]ffonction glob:
sh -cxvf 'echo file[1-5]'
SORTIE
echo file[1-5]
+ echo 'file[1-5]'
file[1-5]
Ainsi, lorsque vous exécutez une commande dans un shell configuré avec des options par défaut comme mv *le shell se développe dans le *mot une liste d'arguments de tous les fichiers du répertoire en cours triés en fonction des paramètres régionaux. Il effectue l'appel système exec(ve)pour mv(essentiellement) avec cette liste d'arguments ajoutée. Obtient donc mvtous les arguments lorsque le shell les globule et les trie. En plus de faire stracepour voir ces effets, vous pouvez utiliser à nouveau le débogage comme:
sh -s -- mv * <<\SCRIPT
sed -n l /proc/$$/cmdline
echo "$@"
SCRIPT
Fondamentalement, le shell s'exécute mvavec le contenu du répertoire (s'il n'est pas vide et n'inclut pas les fichiers / dossiers commençant par .) comme liste d'arguments. mvest spécifié pour interpréter Posix son dernier argument en tant que répertoire si elle est invoquée avec plus de deux arguments - de la même manière lnest (parce que, en fait, ils sont des outils incroyablement similaires dans la fonction sous - jacente) .
Dans le premier formulaire de synopsis, l' mvutilitaire doit déplacer le fichier nommé par l'opérande source_file vers la destination spécifiée par target_file . Cette première forme de synopsis est supposée lorsque l'opérande final ne nomme pas de répertoire existant et n'est pas un lien symbolique faisant référence à un répertoire existant. Dans ce cas, si source_file nomme un fichier non-répertoire et target_file se termine par un /slashcaractère de fin , mvdoit traiter cela comme une erreur et aucun opérande source_file ne sera traité.
Dans le deuxième formulaire de synopsis, mvdoit déplacer chaque fichier nommé par un opérande source_file vers un fichier de destination dans le répertoire existant nommé par l' opérande target_dir , ou référencé si target_dir est un lien symbolique faisant référence à un répertoire existant. Le chemin de destination pour chaque fichier source doit être la concaténation du répertoire cible, un seul /slashcaractère si la cible ne s'est pas terminée par un /slashet le dernier composant de chemin d'accès du fichier source . Cette seconde forme est supposée lorsque l'opérande final nomme un répertoire existant.
Donc, si *étend à:
deux fichiers
Vous ne devriez avoir qu'un seul fichier, qui est le premier renamedau second après le second unlinked.
un ou plusieurs fichiers suivis en dernier par un répertoire ou un lien vers un
Vous ne devriez avoir qu'un seul répertoire ou un lien vers un, c'est là que tout le contenu précédent de son parent vient d'être déplacé.
rien d'autre
Vous devriez avoir un message d'erreur et un soupir de soulagement satisfaisant.
oui, en utilisant le vieux shmachin, j'ai oublié de déboguer avec ça, mais en ce qui concerne ma question d'origine, cela signifie que le problème n'est pas le shell mv? C'est méchant, c'est plus simple que je ne le pensais à l'origine mais c'est un vrai piège.
user2485710
5
@ user2485710, le point clé est que sous Unix, le shell est responsable de l'expansion des caractères génériques avant de passer les arguments de la ligne de commande à la commande. Sous Windows, chaque commande doit développer elle-même les caractères génériques. Cela permet aux shells Unix d'offrir des fonctionnalités avancées de caractères génériques qui fonctionnent avec n'importe quelle commande.
cjm
2
@mikeserv étant donné que ces fichiers n'étaient pas si importants, je me suis précipité pour nettoyer et passer à l'étape suivante, mais je peux confirmer les choses telles qu'elles apparaissent maintenant dans ma modification de mon premier message / question.
user2485710
6
@mikeserv tl; dr, *n'est-ce pas un seul argument? Droite? :)
Bernhard
1
@Bernhard - en fait, c'est le seul cas que je n'ai pas couvert - car il se pourrait .
mikeserv
18
Tout d'abord, le shell s'étend ./*à tous les fichiers du répertoire courant (à l'exception des fichiers commençant par un point).
s'il n'y a pas ou un seul fichier: mvéchoue
s'il y a deux fichiers: le premier est déplacé vers le second (qui se perd donc)
s'il y a plus de deux fichiers:
si le dernier est un répertoire: tous les fichiers sont déplacés dans ce répertoire
Merci. comment savoir quel sous-répertoire est "le dernier"?
Tim
7
Il suffit d'appeler echo ./*pour voir l'ordre utilisé par votre shell (généralement alphabétique).
jofel du
S'il échoue avec un fichier, pourquoi ne le dit-il pas?
Noumenon
@Noumenon Je ne comprends pas votre commentaire. mvrenvoie un message d'erreur s'il est appelé avec un seul argument.
jofel
Vous avez raison. La même commande de mon histoire donne maintenant, missing destination file operand after *filename*même si hier ce n'était pas le cas.
Noumenon
4
Lorsque vous tapez mv ./*, votre shell se développe ./*avant de s'exécuter mv.
Quelques points à noter:
Si ./*est développé en moins de 2 arguments, cela mvproduira logiquement une erreur.
./* sera généralement développé dans chaque fichier (y compris le répertoire) présent dans le répertoire actuel et ne commençant pas par un point.
Vous pouvez contrôler ce qui se ./*développe en lisant la documentation de votre shell ( man 7 globest un point d'entrée vers la rubrique). Différents obus auront différentes options.
Le shell étend le caractère générique *à une liste de contenu de répertoire. Le shell transmet ensuite cette liste complète à la commande. La commande ne voit jamais *.
La commande mv file1 file2 ... filen directorydéplacera file1 ... filen dans le répertoire.
Exemple
Ici, je fais un répertoire de test contenant trois fichiers
$ mkdir t
$ cd t
$ echo a>a; echo b>b; echo c>c
$ ls
a b c
Vous ne pouvez pas déplacer plusieurs fichiers dans un seul fichier
$ mv *
mv: target `c' is not a directory
Ajoutons un sous-répertoire
$ mkdir d
Vous pouvez déplacer plusieurs fichiers dans un sous-répertoire
Il mv file1 file2 ... filen directoryest très peu probable que la commande ait quoi que ce soit à voir *.
mikeserv
@Mike: Ma réponse souligne que la dernière étape de l' expansion du shell transforme efficacement ce dernier en premier. Ne pas savoir que cela semble être à l'origine de la confusion du PO.
RedGrittyBrick
oui, mais mon point est la coquille ne serait pas étendre *à file dirmoins qu'il y ait lieu dans lequel dsuit f.
mikeserv
@mikeserv: Il existe un tel environnement local, mon exemple est un copier-coller de Putty se connectant à un système CentOS 5.6 GNU / Linux.
RedGrittyBrick
quel endroit est-ce? votre exemple est a b c dqui ne l'est pas file ... dir. J'ai seulement commenté parce que vous ne mentionnez le genre nulle part et dites seulement que cela *devient file ... dirce qui ne se produit pas.
mv
j'accepte un argument" Ce n'est pas le cas. Il accepte tous les arguments que le shell lui a transmis après avoir développé le*
.Réponses:
Si le dernier argument était un répertoire, vous venez de déplacer tous les fichiers et répertoires de votre répertoire de travail actuel (sauf ceux dont les noms commencent par des points) dans ce répertoire. S'il y avait deux fichiers, le premier fichier peut avoir écrasé le deuxième fichier.
Voici quelques démonstrations:
Plus de deux fichiers et le dernier argument est un fichier
Plus de deux fichiers et le dernier argument est un répertoire
Deux fichiers
Plus d'explications
Le shell développe le glob (
*
) en arguments pourmv
. Le glob est généralement développé par ordre alphabétique.mv
voit toujours une liste de fichiers et de répertoires. Il ne voit jamais le globe lui-même.La commande
mv
prend en charge deux types de déplacement. L'un estmv file ... directory
. L'autre estmv old-file-name new-file-name
(oumv old-file-name directory/new-file-name
).la source
Je vais d'abord faire une base de test - 5 fichiers et un dossier:
Ensuite, je vais exécuter une commande de test. L'
-v
option spécifie que je veux que chaque commande exécutée par le shell soit impriméestderr
. L'-x
option spécifie que je veux que le même soit impriméstderr
- mais je veux que ce soit fait après que la commande soit évaluée mais avant que le shell ne l'exécute.SORTIE
Vous voyez donc que la commande que j'alimente le shell est
echo mv *
et que la commande que le shell exécute après avoir*
été développée estecho mv
suivie par tous ces fichiers et le dossier.Par défaut, le shell étendra les globes comme:
SORTIE
Ceci est le résultat de la
set [+-]f
fonction glob:SORTIE
Ainsi, lorsque vous exécutez une commande dans un shell configuré avec des options par défaut comme
mv *
le shell se développe dans le*
mot une liste d'arguments de tous les fichiers du répertoire en cours triés en fonction des paramètres régionaux. Il effectue l'appel systèmeexec(ve)
pourmv
(essentiellement) avec cette liste d'arguments ajoutée. Obtient doncmv
tous les arguments lorsque le shell les globule et les trie. En plus de fairestrace
pour voir ces effets, vous pouvez utiliser à nouveau le débogage comme:SORTIE
Et de façon portative:
SORTIE
Fondamentalement, le shell s'exécute
mv
avec le contenu du répertoire (s'il n'est pas vide et n'inclut pas les fichiers / dossiers commençant par.
) comme liste d'arguments.mv
est spécifié pour interpréter Posix son dernier argument en tant que répertoire si elle est invoquée avec plus de deux arguments - de la même manièreln
est (parce que, en fait, ils sont des outils incroyablement similaires dans la fonction sous - jacente) .Assez
echo
cependant:SORTIE
Tous les fichiers ont été déplacés dans l'argument final, car il s'agit d'un dossier. Et si ce n'est pas un dossier?
SORTIE
Voici comment POSIX
mv
doit se comporter dans ce cas:Donc, si
*
étend à:deux fichiers
renamed
au second après le secondunlinked
.un ou plusieurs fichiers suivis en dernier par un répertoire ou un lien vers un
rien d'autre
la source
sh
machin, j'ai oublié de déboguer avec ça, mais en ce qui concerne ma question d'origine, cela signifie que le problème n'est pas le shellmv
? C'est méchant, c'est plus simple que je ne le pensais à l'origine mais c'est un vrai piège.*
n'est-ce pas un seul argument? Droite? :)Tout d'abord, le shell s'étend
./*
à tous les fichiers du répertoire courant (à l'exception des fichiers commençant par un point).mv
échouemv
échoue.la source
echo ./*
pour voir l'ordre utilisé par votre shell (généralement alphabétique).mv
renvoie un message d'erreur s'il est appelé avec un seul argument.missing destination file operand after *filename*
même si hier ce n'était pas le cas.Lorsque vous tapez
mv ./*
, votre shell se développe./*
avant de s'exécutermv
.Quelques points à noter:
./*
est développé en moins de 2 arguments, celamv
produira logiquement une erreur../*
sera généralement développé dans chaque fichier (y compris le répertoire) présent dans le répertoire actuel et ne commençant pas par un point../*
développe en lisant la documentation de votre shell (man 7 glob
est un point d'entrée vers la rubrique). Différents obus auront différentes options.la source
Voici une réponse plus courte:
Le shell étend le caractère générique
*
à une liste de contenu de répertoire. Le shell transmet ensuite cette liste complète à la commande. La commande ne voit jamais*
.La commande
mv file1 file2 ... filen directory
déplacera file1 ... filen dans le répertoire.Exemple
Ici, je fais un répertoire de test contenant trois fichiers
Vous ne pouvez pas déplacer plusieurs fichiers dans un seul fichier
Ajoutons un sous-répertoire
Vous pouvez déplacer plusieurs fichiers dans un sous-répertoire
la source
mv file1 file2 ... filen directory
est très peu probable que la commande ait quoi que ce soit à voir*
.*
àfile dir
moins qu'il y ait lieu dans lequeld
suitf
.a b c d
qui ne l'est pasfile ... dir
. J'ai seulement commenté parce que vous ne mentionnez le genre nulle part et dites seulement que cela*
devientfile ... dir
ce qui ne se produit pas.