Quelque chose que je pense devoir absolument savoir: si je ls <something>
, rm <something>
supprimera-t-il exactement les mêmes fichiers que ls
name__? Existe-t-il des circonstances dans lesquelles rm
pourrait supprimer des fichiers que ls
ne montrait pas? (Ceci est dans le 18.04 bash)
Edit: merci à tous ceux qui ont répondu. Je pense que la réponse complète est une combinaison de toutes les réponses, aussi j’ai accepté la réponse la plus votée comme "la réponse".
Des choses inattendues que j'ai apprises en cours de route:
ls
n'est pas aussi simple qu'on pourrait le penser dans le traitement de ses argumentsls
name__Eh bien, ls
et rm
agissent sur les arguments qui leur sont transmis.
Ces arguments peuvent être un simple fichier, donc ls file.ext
et rm file.ext
agissent sur le même fichier et le résultat est clair (liste le fichier/supprime le fichier).
Si l'argument est un répertoire, ls directory
répertorie le contenu du répertoire, alors que rm directory
ne fonctionnera pas tel quel (c'est-à-dire que rm
sans drapeaux ne peut pas supprimer les répertoires, tandis que Si vous faites rm -r directory
, il supprime récursivement tous les fichiers sous directory
et le répertoire lui-même).
Mais gardez à l'esprit que les arguments en ligne de commande peuvent être soumis à l'expansion du shell , de sorte que ce n'est pas toujours garanti que les mêmes arguments sont transmis aux deux commandes si elles contiennent des caractères génériques, des variables, la sortie d'autres commandes, etc.
Comme exemple extrême, pensez à ls $(Rand).txt
et à rm $(Rand).txt
, les arguments sont "les mêmes" mais les résultats sont assez différents!
Si vous pensez à quelque chose comme ls foo*.txt
vs. rm foo*.txt
, alors oui, ils afficheront et supprimeront les mêmes fichiers. Le shell développe le glob et le passe à la commande en question. Les commandes fonctionnent sur les fichiers répertoriés. On les énumère, on les enlève.
La différence évidente est que si l'un de ces fichiers se trouvait être un répertoire, ls
en listerait le contenu, mais rm
ne réussirait pas à le supprimer. Ce n'est généralement pas un problème, puisque rm
enlèverait moins que ce qui était montré par ls
.
Le gros problème provient de exécuter ls *
ou rm *
dans un répertoire contenant noms de fichiers commençant par un tiret. Ils développeraient les lignes de commande des deux programmes comme si vous les écriviez vous-même, et ls
prendrait -r
comme signifiant "ordre de tri inversé", tandis que rm
prendrait -r
comme signifiant une suppression récursive. La différence est importante si vous avez des sous-répertoires d’au moins deux niveaux. (ls *
affichera le contenu des répertoires de premier niveau, mais rm -r *
dépassera également le premier sous-niveau.)
Pour éviter cela, écrivez des globs permissifs avec un ./
en tête pour indiquer le répertoire en cours, et/ou mettez un --
pour signaler la fin du traitement des options avant le glob (c'est-à-dire rm ./*
ou rm -- *
).
Avec un glob comme *.txt
, ce n'est pas un problème car le point est un caractère d'option invalide et causera une erreur (jusqu'à ce que quelqu'un développe les utilitaires pour en inventer un sens), mais il est toujours plus sûr d'y placer le ./
.
Bien sûr, vous pouvez également obtenir des résultats différents pour les deux commandes si vous avez modifié les options de globing du shell ou créé/déplacé/supprimé des fichiers entre les commandes, mais je doute que vous ayez pensé à l'un de ces cas. (Traiter avec des fichiers nouveaux/déplacés serait extrêmement compliqué à faire en toute sécurité.)
Laissant de côté le comportement de Shell, concentrons-nous uniquement sur ce que rm
et ls
peuvent traiter eux-mêmes. Au moins un cas où ls
indiquera ce que rm
ne peut pas supprimer implique des autorisations de répertoire, et l'autre - répertoires spéciaux .
et ..
.
rm
est une opération sur un répertoire, car en supprimant un fichier, vous modifiez le contenu du répertoire (ou, autrement dit, la liste des entrées de répertoire, car d irectory n'est rien d'autre qu'une liste de noms de fichiers et d'inodes ). Cela signifie que vous avez besoin d'autorisations d'écriture sur un répertoire. Même si vous êtes le propriétaire du fichier , sans autorisations de répertoire, vous ne pouvez pas supprimer de fichiers. Le reverse est également vrai : rm
peut supprimer les fichiers pouvant appartenir à d'autres personnes, si vous êtes le propriétaire du répertoire.
Donc, vous pouvez très bien avoir lu et exécuté des permissions sur un répertoire, ce qui vous permettra de parcourir le répertoire et d’afficher tout son contenu, par exemple ls /bin/echo
, mais vous ne pouvez pas rm /bin/echo
à moins d’être propriétaire de /bin
ou d’élever vos privilèges avec Sudo
name__.
Et vous verrez des cas comme celui-ci partout. En voici un exemple: https://superuser.com/a/331124/418028
Les répertoires .
et ..
constituent un autre cas particulier. Si vous faites ls .
ou ls ..
, il vous montrera avec plaisir le contenu, mais rm
name __ 'ing n'est pas autorisé:
$ rm -rf .
rm: refusing to remove '.' or '..' directory: skipping '.'
Si vous tapez ls *
puis rm *
, il est possible que vous supprimiez plus de fichiers que ls
- ils ont peut-être été créés dans un intervalle de temps très court entre la fin de ls
et le début de rm
.
ls *
et rm *
ne sont pas responsables du développement du glob - c'est le shell qui le fait avant de le transmettre à la commande.
Cela signifie que vous pouvez utiliser la commande any avec la liste de fichiers étendue - je voudrais donc utiliser quelque chose qui fait le moins possible.
Donc, une meilleure façon de faire cela (ou du moins une autre façon) est de sauter l’intermédiaire.
echo *
vous montrera exactement ce qui serait passé à votre commande rm
.
Si vous ne faites que ls
au lieu de ls -a
, oui rm
peut supprimer les fichiers cachés que vous n'avez pas vus avec ls
sans -a
.
Exemple:
Selon :
dir_test
├── .test
└── test2
ls dir_test
: affichera uniquement test2
ls -A dir_test
: affichera test2 + .test
rm -r dir_test
: va tout supprimer (.test + test2)
J'espère que cela vous aidera.
Que diriez-vous:
$ mkdir what
$ cd what
$ mkdir -p huh/uhm ./-r
$ ls *
uhm
$ rm *
$ ls
-r
$ ls -R
.:
-r
./-r:
Fondamentalement, les caractères génériques étendus à des éléments commençant par -
(ou des éléments saisis manuellement commençant par -
mais qui ressemble un peu plus à de la triche) peuvent être interprétés différemment par ls
et rm
.
There are Edge cas où ls
n'est pas ce que rm
supprime. Un cas extrême, mais heureusement bénin, est si l'argument que vous passez est un lien symbolique vers un répertoire: ls
vous montrera tous les fichiers du répertoire en lien symbolique, tandis que rm
supprimera le lien symbolique en laissant le répertoire d'origine et son contenu intacts:
% ln -s $HOME some_link
% ls some_link # Will display directory contents
bin lib Desktop ...
% rm some_link
% ls $HOME
bin lib Desktop ...
Il y a déjà beaucoup de bonnes réponses, mais je veux ajouter un aperçu plus profond.
Posez-vous la question: combien de paramètres sont passés à ls
, si vous écrivez
ls *
...? Notez que la commande ls
n'obtient pas le *
en tant que paramètre s'il existe des fichiers pour lesquels *
peut être développé. Au lieu de cela, le Shell effectue d'abord une analyse par globalisation avant d'appeler la commande. Par conséquent, la commande ls
obtient en fait autant de paramètres qu'il y a de fichiers correspondant à cette analyse. Pour supprimer la suppression, citez le paramètre.
Cela est vrai pour toute commande: echo *
vs echo '*'
.
Il existe un script, appelez-le countparams.sh
pour tester l'effet. Il vous indique le nombre de paramètres passés et les répertorie.
#!/bin/bash
echo "This script was given $# parameters."
arr=( "$@" )
for ((i=0;i<$#;i++)); do
echo "Parameter $((i+1)): ${arr[$i]}"
done
Rendez-le exécutable et exécutez ./countparams.sh *
. Apprenez de sa sortie!
Le glob se développera de la même manière les deux fois si le contenu du répertoire est le même à ces deux moments différents.
Si vous voulez vraiment vérifier ce qui sera supprimé, utilisez rm -i *.txt
. Il vous demandera séparément pour chaque fichier avant de (essayer de) le supprimer.
Ceci est garanti pour être sûr contre les conditions de course:ls *.txt
/un nouveau fichier est créé/rm *.txt
parce que vous êtes invité à chaque fichier par le même programme que celui qui effectue la suppression.
Cela est trop lourd pour une utilisation normale, et si vous alias rm
à rm -i
, vous utiliserez assez souvent \rm
ou rm -f
. Mais il convient au moins de mentionner qu’il existe une solution à la situation de concurrence critique. (Il est même portable sur des systèmes non-GNU: POSIX rm(1)
spécifie l'option -i
.)
Une autre option serait un tableau bash: to_remove=(*.txt)
, puis demandez à l'utilisateur de confirmer (peut-être après avoir effectué ls -ld -- "${to_remove[@]}"
), puis rm -- "${to_remove[@]}"
. Ainsi, l’expansion globale n’est effectuée qu’une seule fois et la liste est transmise intégralement à rm
name__.
Une autre option pratiquement utilisable est GNU rm -I
( page de manuel ), qui vous invite à supprimer plus de 4 éléments. (Mais ne vous montre pas la liste, mais le total.) J'utilise alias rm='rm -I'
sur mon bureau.
C'est une bonne protection contre le retour du gros doigt avec un motif à moitié tapé qui correspond trop. Toutefois, l'utilisation de ls
est généralement préférable dans un répertoire que vous possédez ou sur un système mono-utilisateur, et lorsqu'il n'y a pas de processus en arrière-plan qui pourrait créer de manière asynchrone de nouveaux fichiers. Pour vous protéger des gros doigtés, ne tapez pas rm -rf /foo/bar/baz
de gauche à droite. rm -rf /
est un cas spécial, mais rm -rf /usr
ne l'est pas! Laissez la partie -rf
ou commencez par ls
et ajoutez uniquement la partie rm -rf
après la saisie du chemin.