Est-ce que ls listera toujours les fichiers que rm va supprimer?


33

Quelque chose que je sens que je dois savoir à coup sûr: si je ls <something>, vais rm <something>supprimer exactement les mêmes fichiers lsaffichés? Existe-t-il des circonstances dans lesquelles des rmfichiers non visibles pourraient être supprimés ls? (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 arguments
  • Dans une simple installation sans fioritures d’Ubuntu, alias .bashrc ls
  • Ne nommez pas vos fichiers en commençant par un tiret, car ils peuvent ressembler à des arguments de commande. Nommer one -r le demande!

8
Je suis un peu surpris de rmne pas avoir de --dry-rundrapeau ...
fkraiem

20
@Rinzwind Pourquoi serait find -deletemieux que rm? Vous dites "Voilà pourquoi" , mais je ne vois absolument pas ce que cela signifie. Notez également que votre findinvocation supprimera tous les fichiers de manière récursive dans le répertoire actuel, où rmils supprimeront simplement les fichiers du répertoire immédiat. Aussi -name *est un no-op. Dans l’ensemble, votre conseil
me laisse

3
@marcelm Je pense que le conseil pour utiliser findest parce que vous pouvez l'exécuter, voir tous les fichiers, puis exécuter la même commande avec -delete. Puisque vous avez déjà vu les résultats find, il ne devrait y avoir aucune ambiguïté quant à ce qui sera supprimé (j'aimerais en savoir plus à ce sujet sous la forme d'une réponse)
Scribblemacher

5
@Scribblemacher "... vous pouvez l'exécuter, voir tous les fichiers, puis exécuter la même commande avec -delete" - Mais comment cela vaut-il mieux que d'exécuter ls <filespec>, suivi de rm <filespec>(ce que l'OP sait déjà faire)?
Marcelm

12
@Rinzwind "Cela résout la réponse de choroba pour l'instant où un fichier est créé après ls et avant rm." - Non. Si vous exécutez d' find ... -printabord pour vérifier quels fichiers seront supprimés find ... -delete, vous supprimerez tout de même les fichiers créés entre les deux commandes. Si vous utilisez les deux -printet -delete, vous n'obtenez pas de confirmation, mais simplement un rapport a posteriori de ce qui a été supprimé (et vous pourriez aussi bien l'utiliser rm -v).
Marcel

Réponses:


40

Eh bien, à la fois lset rmfonctionner sur les arguments qui sont passés à eux.

Ces arguments peuvent être un fichier simple, si ls file.extet rm file.extfonctionnent sur le même fichier et le résultat est clair (liste le fichier / supprimer le fichier).

Si , au contraire l' argument est un répertoire, ls directoryliste le contenu du répertoire tout rm directoryne fonctionne pas comme est -à- dire ( rmsans drapeaux ne peuvent pas supprimer des répertoires, alors que si vous le faites rm -r directory, il supprime récursive tous les fichiers sous directory et le répertoire lui - même ).

Mais gardez à l'esprit que les arguments de ligne de commande peuvent être soumis à une expansion du shell , il n'est donc pas toujours garanti que les mêmes arguments soient transmis aux deux commandes s'ils contiennent des caractères génériques, des variables, des sorties d'autres commandes, etc.

Comme exemple extrême, pensez ls $(rand).txtet rm $(rand).txtles arguments sont "les mêmes" mais les résultats sont très différents!


3
Pensez également à lsvs rm *où il y a des fichiers "cachés" (point), même si ce n'est pas du tout une comparaison juste, car je n'ai pas écrit ls *. Mais cela rmn'a pas de sens en lui-même, de sorte que tout est en réalité des pommes et des oranges. Si j'ai bien compris, c'est le coeur de votre réponse, alors bon travail :)
Courses de légèreté avec Monica

5
Un autre commentaire sur lsvs rm -r: la commande ls <directory>ne sera pas afficher les fichiers cachés à l' intérieur du répertoire, mais rm -r <directory> va supprimer même les fichiers cachés.
Daniel Wagner

1
@LightnessRacesinOrbit lsne les listera pas (sauf s'il est associé à un alias ls -a), et rm *ne les supprimera pas (à moins que vous ne l'ayez dotglobdéfini).
Arrêtez de blesser Monica le

20

Si vous pensez à quelque chose comme ls foo*.txtvs 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 listé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, lsson contenu serait répertorié, mais rmil ne parviendrait pas à le supprimer. Ce n'est généralement pas un problème, car rmsupprimerait moins que ce qui a été montré par ls.

Le gros problème vient de la course ls *ou rm *dans un répertoire contenant des noms de fichiers commençant par un tiret . Ils s’étendraient aux lignes de commande des deux programmes comme si vous les écriviez vous-même, et lsvoulaient -rdire «ordre de tri inversé», alors rmqu’il faudrait -run retrait récursif. La différence est importante si vous avez des sous-répertoires d’au moins deux niveaux. ( ls *montrera le contenu des répertoires de premier niveau, mais rm -r *tout dépassera également le premier niveau.)

Pour éviter cela, écrivez des variables globales permissives avec un interligne ./pour indiquer le répertoire actuel et / ou mettez une --pour signaler la fin du traitement des options avant la variable globale (c'est rm ./*-à- dire ou rm -- *).

Avec un glob comme *.txt, cela n’est en réalité pas un problème, car le point est un caractère d’option non valide et provoquera une erreur (jusqu’à ce que les utilitaires développent leur utilité pour en inventer un sens), mais il est toujours plus sûr de le mettre de ./toute façon.


Bien sûr, vous pouvez également obtenir des résultats différents pour les deux commandes si vous modifiez les options de déplacement du shell ou les fichiers créés / déplacés / supprimés entre les commandes, mais je doute que vous ayez pensé à l'un ou l'autre de ces cas. (Traiter avec des fichiers nouveaux / déplacés serait extrêmement compliqué à faire en toute sécurité.)


1
Merci. Cela semble mettre en évidence un problème avec la syntaxe complète de la ligne de commande: si vous nommez des fichiers commençant par un tiret, vous naviguez dans des eaux dangereuses. Qui sait quelles commandes vous pourriez utiliser dans le futur, longtemps après que vous ayez oublié les - fichiers.
B.Tanner

1
@ B.Tanner, oui. En un sens, le problème est que les arguments de la ligne de commande ne sont que de simples chaînes. Si le système était conçu aujourd'hui, il pourrait y avoir plus de structure afin que le programme exécuté puisse savoir si un argument était supposé être un indicateur d'option ou non. Il y a aussi d'autres problèmes qui proviennent de la structure très laxiste des noms de fichiers. Il y a un essai très approfondi sur ce sujet rédigé par le pilote , mais je dois avertir que la lecture de ce texte va faire mal. (Soit du détail, soit de l'
extrême horreur

3
Je ne peux pas résister, vous me l'avez vendu, je vais le lire maintenant, avec des souvenirs du temps où j'ai réussi à obtenir un caractère de retour chariot à la fin de nombreux noms de fichiers (en essayant de voir si je pouvais construisez un fichier batch Windows pouvant également être exécuté en tant que script bash ... Je ne l'avais pas vu venir!)
B.Tanner

17

Laissant de côté le comportement de la coquille, concentrons-nous uniquement sur ce rmque nous lspouvons gérer nous-mêmes. Au moins un cas dans lequel lsmontrera ce qui rmne peut pas être supprimé implique des autorisations de répertoire, et l'autre - des répertoires spéciaux .et ...

Autorisations de dossier

rmest une opération sur un répertoire, car en supprimant un fichier, vous modifiez le contenu du répertoire (ou en d’autres termes, la liste des entrées de répertoire, car le répertoire n’est rien de plus 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. L' inverse est également vrai : vous rmpouvez supprimer les fichiers pouvant appartenir à d'autres personnes, si vous êtes propriétaire du répertoire.

Ainsi, vous pouvez très bien avoir lu et exécuté des autorisations sur un répertoire, ce qui vous permettra de parcourir le répertoire et d'afficher son contenu sans problème, par exemple ls /bin/echo, mais vous ne pouvez pas le faire à rm /bin/echomoins d'être propriétaire /bin ou d'élever vos privilèges sudo.

Et vous verrez des cas comme celui-ci partout. Voici un exemple: https://superuser.com/a/331124/418028


Annuaires spéciaux '.' et '..'

Un autre cas particulier est .et ..répertoires. Si vous le faites ls .ou ls .., il vous montrera avec plaisir le contenu, mais il rmn'est pas autorisé de le faire:

$ rm -rf .
rm: refusing to remove '.' or '..' directory: skipping '.'

15

Si vous tapez ls *et puis rm *, il est possible que vous supprimiez plus de fichiers que ceux lsindiqués - ils ont peut-être été créés dans un intervalle de temps très court entre la fin lset le début de rm.


1
C'est un cas probable pour /tmplequel de nombreuses applications peuvent créer des fichiers temporaires. C'est donc toujours une possibilité dans les deux commandes *. Cependant, certaines applications rendent également les fichiers anonymes en unlink()les gardant tout en gardant le descripteur de fichier ouvert. Il est donc possible qu’il apparaisse dans le fichier, ls *mais rm *ne le détecte pas.
Sergiy Kolodyazhnyy

1
@ OrangeDog Vous manquez le point. Nous parlons de condition de course
Sergiy Kolodyazhnyy

1
@OrangeDog Je devrai vérifier cela puisque je suis au téléphone en ce moment, mais le problème est toujours valable, même *s'il existe une différence entre ce qui lsserait affiché et ce qui rmfonctionne, car la liste du contenu des répertoires a changé entre les deux.
Sergiy Kolodyazhnyy

1
Même si vous n'exécutez qu'une seule commande, il existe une condition de concurrence critique entre le développement des arguments et leur suppression.
Arrêtez de blesser Monica le

1
@ OrangeDog Je peux être d'accord avec cela. Etant donné que l’extension avec joker fonctionne sur les noms de fichiers existants, oui, il existe une condition de concurrence entre le shell qui développe le joker et une commande qui le traite, ce qui ls *pourrait en fait afficher le nom de fichier qui a déjà disparu.
Sergiy Kolodyazhnyy

9

ls *et rm *ne sont pas responsables de l'expansion du glob - cela est fait par le shell avant de le passer à la commande.

Cela signifie que vous pouvez utiliser n’importe quelle commande avec la liste de fichiers étendue. Par conséquent, j’utiliserai 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 *va vous montrer exactement ce qui serait passé à votre rmcommande.


2
Merci, j'aime bien l'idée d'utiliser echo au lieu de ls dans ce scénario.
B.Tanner

3
Ou printf "%s\n" *pour obtenir une vue non ambiguë sur les noms de fichiers avec des espaces. (Ou %qau lieu de gérer les sauts de ligne et les caractères de contrôle aussi, au détriment d'une sortie plus laide.)
ilkkachu

4

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 entrés manuellement commençant par -mais qui ressemblent un peu plus à de la triche) peuvent être interprétés différemment par lset rm.


4

Il y a des cas extrêmes où ce qui lsest montré n'est pas ce qui est rméliminé. Un cas extrême, mais heureusement bénin, est si l'argument que vous passez est un lien symbolique vers un répertoire: lsvous montrera tous les fichiers du répertoire contenant un lien symbolique , tout rmen supprimant le lien symbolique, en laissant le répertoire original 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 ...

1
Huh. ln -s $HOME some_link; ls some_linksorties some_link@pour moi, mais je ai lsaliasé à ls -F. Apparemment, -Fmodifie le comportement pour afficher le lien au lieu de le déréférencer. Je ne m'attendais pas à ça.
marcelm

Effectivement! Aussi, ls -lpar exemple, cible le lien, pas la destination ... il doit y avoir des moyens d'inspecter le lien lui-même.
alexis

1
Ajouter des options à la question ouvre trop de possibilités - ma réponse concerne le comportement non modifié de lset rm.
alexis

Si vous dites ls some_link/,  ls -H some_linkou ls -L some_link, le répertoire lié sera répertorié, même si vous ajoutez -Fou -l. Réciproquement (en quelque sorte), -ddit de regarder un répertoire plutôt que son contenu; comparer ls -l /tmpet ls -ld /tmp.
G-Man dit 'Réintégrez Monica' le

Bien sûr, vous pouvez ajouter des indicateurs qui changent le comportement de ls. En gros, vous montrez pourquoi énumérer des comportements avec différents drapeaux ne vaut pas la peine de poser cette question ...
alexis

4

Si vous ne le faites qu'à la lsplace de ls -a, oui rmpeut supprimer les fichiers cachés que vous n'avez pas vus lssans -a.

Exemple :

Selon :

dir_test
├── .test
└── test2

ls dir_test : affichera seulement test2

ls -A dir_test : affichera test2 + .test

rm -r dir_test : va supprimer tout (.test + test2)

J'espère que cela vous aidera.


Pouvez vous donner un exemple? Parce que normalement, rm *ne supprimera pas les fichiers de points. Si c'est le cas, ls *leur montrera également.
marcelm

Non, ls *ne pas afficher les fichiers cachés.
DevHugo

Mais oui c'est un peu confus, j'ai ajouté quelques exemples.
DevHugo

1
ls -alistera ., .., .testet test2. Vous voudrez peut-être changer votre exemple à utiliser ls -A, qui répertorie tout sauf sauf . et ..(c'est-à-dire seulement .testet test2).
G-Man dit 'Réintégrez Monica' le

3

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 lscommande n'obtient pas le *paramètre as s'il existe des fichiers *pouvant être développés. Au lieu de cela, le shell commence par effectuer une analyse de globalisation avant d'appeler la commande. Cette lscommande obtient donc 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.shpour 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 courez ./countparams.sh *. Apprenez de sa sortie!


1

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 contre les conditions de concurrence:
        ls *.txt/ un nouveau fichier est créé / rm *.txt
parce que chaque fichier est invité par le même programme que celui qui effectue la suppression.


Ceci est trop lourd pour une utilisation normale, et si vous alias rmpour rm -i, vous vous retrouverez en utilisant \rmou rm -fassez souvent. 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' -ioption .)

Une autre option serait un tableau bash:, to_remove=(*.txt)puis demandez à l'utilisateur de confirmer (peut-être après ls -ld -- "${to_remove[@]}"), puis rm -- "${to_remove[@]}". Ainsi, l’expansion globale n’est effectuée qu’une fois et la liste est transmise textuellement rm.

Une autre option pratiquement utilisable est GNU rm -I( page de manuel ), qui vous invite si vous supprimez 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. Mais utiliser en lspremier est généralement bon 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 y créer de manière asynchrone de nouveaux fichiers. Pour éviter les gros doigtés, ne tapez pas rm -rf /foo/bar/bazde gauche à droite. rm -rf /est spécial, mais rm -rf /usrne l'est pas! Laissez la -rfpartie ou commencez avec elle lset ajoutez-la seulement rm -rfaprès avoir tapé le chemin.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.