Empêcher l'utilisateur de taper un espace accidentel entre rm et le caractère générique


29

Intention:

rm -rf string*

Problème:

rm -rf string *

Le premier cas est une utilisation légitime et courante de rm, une petite faute de frappe peut causer beaucoup de problèmes dans le second cas. Existe-t-il un moyen simple de se protéger intelligemment contre un caractère générique de fuite ou de début accidentel?


8
Avec un shell récent et bien configuré ( zshou bash) je préfère prendre l'habitude d'appuyer sur la touche de tabulation (pour déclencher l'auto-complétion) avant d'appuyer sur la touche retour
Basile Starynkevitch

2
touch -- -icréera un fichier -iqui sera passé en rmtant que paramètre -ilorsque vous incluez un caractère générique dans vos arguments.
Rétablir Monica - M. Schröder

@ MartinSchröder: Si vous vous trouvez dans le répertoire où le fichier existe.
pause jusqu'à nouvel ordre.

@ MartinSchröder Cela ne fonctionnera que si vous tapez rm *. Si vous tapez rm name *, il se développera rm name -i other names. -ine sera pas traité en option car il ne se trouve pas avant les noms de fichiers.
Barmar

1
En règle générale, un utilisateur ne fait cela qu'une seule fois.
tedder42

Réponses:


37

Un DEBUGpiège pourrait être écrit pour annuler les commandes qui semblent suspectes. Le code suivant, ou un code similaire, peut être ajouté à votre ~/.bashrc:

shopt -s extdebug
checkcommand() {
  if [[ $BASH_COMMAND = 'rm -r'*' *' ]]; then
    echo "Suppressing rm -r command ending in a wildcard" >&2
    return 1
  fi
  # check for other commands here, if you like
  return 0
}
trap checkcommand DEBUG

Ajustez la logique au goût.

(Je ne m'attends pas à ce que cette approche soit utile - trop de façons de gâcher une commande de manière destructive pour les trouver à tester une par une - mais elle fournit une réponse littérale à la question).


47

Il n'y a aucun moyen de sécuriser totalement un système pare-balles. Et en ajoutant "Êtes-vous sûr?" invite aux choses est à la fois contre-productif et conduit à "Bien sûr, je suis sûr." réactions instinctives.

Une astuce que j'ai prise dans un livre au cours des années passées est de faire d'abord, ls -R blah*alors rm -fr blah* si et seulement si la liste qui a été trouvée correspond à ce que je voulais.

Il est assez facile de faire la lscommande d'abord, puis de supprimer ls -Ret de remplacer par rm -fr.

La dernière question est: "Si les informations vous ont été précieuses, où est votre sauvegarde?"


4
Plutôt que la flèche vers le haut, vous pouvez utiliser ^ls -R^rm -rf^et peut-être définir un alias
exussum

FWIW, ls -Rc'est ce que je fais essentiellement tout le temps, mais c'est tellement instinctif à ce stade qu'il a glissé mon esprit conscient lorsque je me suis formé par réponse. Donc +1 pour ça.
JakeGould

Cela a également l'avantage de ne pas faire de mal si vous cognez la touche Entrée, ou cognez votre souris et collez quelque chose avec des sauts de ligne. J'entre parfois des choses dans un ordre moins efficace juste pour plus de sécurité, par exemple cat > .log, puis je reviens et je colle ou tire le nom du fichier avant le .log. Donc à aucun moment je n'ai > valuablefilesur la cmdline.
Peter Cordes

J'utilise rm avec un alias pour rm -i, donc je compose souvent ma commande rm puis je place un \ au début de la ligne. ( \rmévite l'alias-expansion pour rm, car une partie du mot a été citée). Si j'avais besoin d'un -rou -f, ouais je commence parfois par lsau lieu de rm, ou je laisse simplement de côté la -rfpartie. (modifier, comment obtenir un formatage de code barre oblique inverse? bquote bslash bslash bquote échoue.
Peter Cordes

8

Pouvez-vous vous entraîner à utiliser, par exemple, rmrfà la place de rm -rf?

Si c'est le cas, cette fonction bash vous donnera une chance de voir ce qui se passerait réellement avant de confirmer la commande:

rmrf() { echo rm -rf "$@"; read -p "Proceed (y/N)? "; [ "${REPLY,,}" = y ] && rm -rf "$@"; }

Pour rendre cette fonction permanente, ajoutez cette ligne au ~/.bashrcfichier sur chaque ordinateur que vous utilisez.

Comparaison avec l' rmalias commun

Il est courant de définir un alias:

alias rm='rm -i'

Cela a deux limites:

  1. Si vous en venez à dépendre de cet alias, vous serez choqué lorsque vous êtes sur une machine ou dans un environnement qui ne l'a pas. En revanche, essayer de fonctionner rmrfsur une machine sans cette fonction rendra inoffensif command not found.

  2. Cet alias n'est d'aucune aide dans votre cas car l' -foption que vous fournissez sur la ligne de commande remplace l' -ioption dans l'alias.


4
Pas une mauvaise idée. Mais cela dépend de l'installation de cette fonction et de sa disponibilité sur tous les systèmes que cet utilisateur utilise. Supposons donc qu'ils se connectent à un serveur distant pour certains travaux et exécutent la mauvaise rm -rfcommande, que se passe-t-il? La création de pseudo-fonctions comme celle-ci finit par compliquer une solution simple: soyez plus prudent et comprenez ce que sont les autorisations.
JakeGould

4
@JakeGould Et n'utilisez pas rm -f sauf si vous en avez vraiment besoin .
un CVn le

1
Pourquoi s'embêter avec rmrf, au lieu de simplement créer une fonction shell rmqui vérifie les arguments -rou -rf, et adopte une logique spéciale dans ce cas, sinon appelant directement command rm "$@"pour appeler la vraie rmcommande?
Charles Duffy

3
Vous utilisez un nom différent pour ne pas vous tirer une balle dans le pied lorsque votre priorité n'est pas là. Et je suis d'accord avec @ MichaelKjörling, vous n'avez pas besoin -fsi vous n'en avez pas -i. Par exemple, le -ffait de ne pas afficher vous avertit avant de supprimer un fichier en lecture seule.
Peter Cordes

6

Si je suis dans une situation où la suppression des mauvais fichiers est un gros problème, l'une des choses que j'ai faites est de créer un dossier poubelle, comme mkdir trashcan , et j'ai un script rmTrashcanqui a un rm -rf trashcan/*ou rm -rf *ou similaire, écrit très soigneusement et vérifié plusieurs fois.

De cette façon, si je fais une erreur, l'erreur est sur une mvcommande, pas sur une rmcommande. Une fois que je fais unls et que je suis sûr de ce que je supprime, un rmTrashcanfait le sale boulot en toute sécurité.

C'était également extrêmement pratique pour une situation où je devais supprimer des sauvegardes. J'ai pu mvun mois entier de sauvegardes dans la corbeille, mvles quelques que je voulais garder (le 1er du mois, le 15 du mois) dans les sauvegardes, puis rmTranshcanle reste. Il est difficile de faire une commande similaire rmseule, et de cette façon, laissez-moi lsles sauvegardes avoir confiance en quels fichiers je laisse derrière moi (plutôt que d'énumérer simplement les fichiers que j'ai l'intention de supprimer)


4

Au lieu de se soucier de deux commandes ( lset rm), je pense qu'un moyen plus simple et plus facile consiste à utiliserfind :

find . -maxdepth 1 -name "string*" -delete

Si vous tapez accidentellement, "string *"il supprimera les fichiers nommés string charsetstring letters (ce que vous souhaitiez de toute façon), mais il ne récupérera pas tous les fichiers comme *dans un shell.

En outre, vous pouvez laisser le -deletepour voir quels fichiers il va supprimer, puis appuyer sur la flèche vers le haut et taper -deleteplus facilement que de taper ^ls -R^rm -rfou d'autres bêtises.


A findfera une recherche imbriquée pour les fichiers qui correspondent au lieu des seuls paramètres explicites déclarés sur la ligne de commande. Je pense que cela manque la cible. Je pourrais toutefois avoir tord.
killermist

3

Existe-t-il un moyen simple de se protéger intelligemment contre un caractère générique de fuite ou de début accidentel?

Pas vraiment. Il est proposé dans une autre réponse que vous pouvez créer une commande personnalisée pour ajouter une invite avant d'exécuter une tâche. Mais le problème avec cette commande personnalisée est qu'elle doit être installée consciemment sur les systèmes sur lesquels vous travaillez. Et même s'il est installé, le problème est que votre réflexe de frapper ypour accomplir la tâche pourrait entrer en jeu.

Cela signifie que tout cela est un problème d'interface utilisateur même si c'est au niveau du texte / de la ligne de commande. Combien de filets de sécurité pensez-vous qu'il y aura pour vous protéger de faire quelque chose que vous ne devriez pas faire? C'est comme une paire de ciseaux: si vous êtes en quelque sorte négligent et glissez et coupez votre main tout en ayant l'intention de couper du tissu ou du papier, qui est en faute? Ou même des feux de circulation et des panneaux d'arrêt: rien n'empêche vraiment un conducteur d'allumer un feu ou d'ignorer les panneaux de signalisation, à part une conscience aiguë de ce qui pourrait se produire s'il adoptait un comportement aussi risqué.

Cela dit, la meilleure solution réaliste réside dans les autorisations système pour les utilisateurs et les groupes. C'est le meilleur / le seul véritable filet de sécurité pour protéger un utilisateur contre lui-même.

Si vous travaillez sur un système où vous êtes le seul à y accéder, vous pourriez être tenté de chmod 777tout, mais ce n'est pas ainsi qu'un système rationnel est configuré. Au lieu de cela, les autorisations devraient ressembler 755à tous les répertoires et 644à tous les fichiers non exécutables. Les fichiers exécutables doivent être 755au moins, mais peut-être même 744si vous souhaitez que les autres lisent mais n'exécutent pas les fichiers.


2

Essayez de composer votre rmcommande initiale sans l' -findicateur, mais avec -iau lieu de cela, qui rmvous demandera pour chaque fichier qu'il a l'intention de supprimer. Pour les petites suppressions récursives, vous pouvez maintenir la ytouche enfoncée, une fois que vous êtes sûr que la commande a été tapée correctement. Pour les suppressions importantes, vous pouvez abandonner l'opération et utiliser l'historique de la ligne de commande pour modifier soigneusement le -ien a -f.


Vous devez en fait appuyer sur y[RETURN]chaque fichier, il n'y a rien que vous puissiez maintenir avec GNU rm. La modification de la -iest la voie à suivre, une fois que vous avez correctement tapé la commande. Ensuite, vous obtenez une invite uniquement sur un fichier en lecture seule, et peut-être d'autres choses.
Peter Cordes

1
Il suffit de l'utiliser rm -Ipour qu'il ne s'affiche qu'une seule fois, et uniquement pour les suppressions potentiellement dangereuses (c'est-à-dire de nombreux fichiers.)
Nick Matteo

1
Dans les forums moins constructifs, ma réponse aurait été quelque chose du genre "Si votre saisie / relecture est si mauvaise, vous n'avez rien à faire avec 'rm -rf' avec des caractères génériques." Je ne connaissais pas le commutateur -I ... a dû être installé il y a moins de 20 ans; 'la dernière fois que j'ai fait' man rm '. :)
Nevin Williams

2

rm a -iet des -Idrapeaux pour confirmer avant chaque suppression. Dans le passé, certaines distributions les ont activées par défaut. C'est une terrible idée. Donnez à l'utilisateur trop de boîtes de dialogue de confirmation pour les opérations normales et il commencera à les confirmer habituellement. Cela déplace simplement l'exigence de «faire attention» (toujours un drapeau rouge) à une nouvelle boîte de dialogue plus agaçante. "Oui. Oui. Oui. Oui! OUI! Bon sang, stupide ordinateur, supprime simplement les fichiers OUI OUI OUI OUI - CRAP JE SIGNIFIE NON! NOOOOOOO!" Il s'agit du problème de dialogue "Oui, mais je voulais dire non". Cette réponse fournit une explication visuelle des raisons pour lesquelles les boîtes de dialogue de confirmation arrivent au mauvais moment.

Le genre d'erreur que vous décrivez est un glissement , "la réalisation d'une action qui n'était pas ce qui était prévu". L'utilisateur reconnaît généralement immédiatement l'erreur et sait exactement comment la corriger. Malheureusement, Unix ne donne pas la possibilité à l'utilisateur, rm supprime le fichier immédiatement. Tous les autres systèmes d'exploitation résolvent ce problème en permettant aux suppressions d'être annulées, au moins pendant un petit moment, par l'utilisation de la corbeille.

Il existe différents systèmes de corbeille pour Unix, et cette réponse regorge de suggestions .

La question est d'alias rm ou non d'alias rm. Avantages de l'aliasing rm ...

  1. Vous ne pouvez pas oublier d’utiliser l’alternative rm.

Contre l'aliasing rm ...

  1. Vous pourriez en venir à compter sur des systèmes qui ne l'ont pas.
  2. Peut causer des problèmes lorsque le disque est presque plein.
  3. Besoin d'une infrastructure pour vider périodiquement la poubelle.
  4. Vous devez être sûr de ne pas interférer avec le comportement attendu de rm dans les programmes.
  5. Pourrait ne pas émuler complètement rm.

Si vous suivez trop loin le premier argument, vous finissez par utiliser vi (pas vim, vi), csh (pas tcsh, csh) et d'autres utilitaires désuets car ils sont universellement disponibles. Pourtant, il existe un risque de sur-personnalisation de votre environnement. Je préfère emporter mes utilitaires avec moi et rendre cela aussi simple que possible. YMMV.

Deux et trois sont des problèmes techniques. Ils peuvent être résolus avec un travail de faucheuse intelligent qui vérifie la taille de la poubelle et nettoie périodiquement les choses, comme un tmpreaper . Cela peut être un travail cron, ou une version plus intelligente peut utiliser les diverses infrastructures d'événements du système de fichiers disponibles sur de nombreuses distributions Linux de bureau. Ce n'est pas simple et encore plus difficile à faire efficacement. Il vaut mieux trouver un système existant que d'essayer d'en créer un.

Le quatrième peut être traité en faisant de votre nouveau rm un alias de shell alias rm='trash', alors cela n'affectera pas les programmes.

Le cinquième est un problème que je laisse au lecteur de résoudre. rm n'a pas beaucoup de commutateurs.


J'ai un alias rm rm -i, mais j'utilise souvent \rmpour obtenir le comportement sans alias. Je tape rm -ichaque fois que je prévois de dire non à l'un des arguments. Je lance parfois la commande avec lscomme commande, puis je ne mets le \ rm que lorsque j'ai terminé. Il n'y a donc aucun moyen que je puisse accidentellement frapper le retour au rm -rf /lieu de/.../file
Peter Cordes

rm -Iou rm --interactive=onceest loin, beaucoup moins ennuyeux que rm -i, et attrape toujours quelque chose de dangereux (il ne demande que lorsqu'il y a plus de 3 fichiers, ou s'il est récursif, et juste UNE FOIS au lieu de POUR CHAQUE FICHIER.)
Nick Matteo

@Kundor Oui, et cela ne change pas l'équation. Il est probablement plus probable que l'utilisateur l'ignore. L'utilisateur a un objectif: supprimer des éléments. L'invite indique "Êtes-vous sûr de vouloir supprimer les éléments que vous venez de me demander de supprimer?" L'utilisateur est toujours fixé sur l'objectif de supprimer des choses, ne vérifiant pas ce qui doit être supprimé, alors ils disent «oui» sans réfléchir. -ipourrait donner à l'utilisateur le temps de modifier ses objectifs. Cette réponse à une question connexe explique tout.
Schwern

@Schwern: mais personne ne peut vivre avec rm -iplus de 30 secondes sans l'éteindre. Alors rm -Ique vous invite uniquement quand il est probable que vous avez foiré; l'invite ne s'affiche que pour les suppressions importantes. Lorsque vous essayez de supprimer quelques éléments et que l'invite s'affiche, vous êtes surpris et réalisez que vous avez accidentellement tapé "foo *".
Nick Matteo

@Kundor La suppression de plus de trois fichiers et la suppression récursive (la confirmation est désactivée par l'habituel que -rfje viens de découvrir) sont de mauvais proxys pour détecter un éventuel échec . Vouloir supprimer un sous-répertoire n'est pas une erreur. Le message n'aide pas car il confirme simplement ce que l'utilisateur a dit de faire sans ajouter d'informations utiles à un moment où l'utilisateur ne cherche pas à vérifier. "rm: supprimer 1 argument récursivement?" "Oui, supprimez le répertoire, je vous ai juste dit de le faire! ... attendez, quel répertoire était-ce? CRAP !!!"
Schwern

2

J'apprends à tout le monde le !$dernier argument de la dernière astuce de commande:

% ls job[XYZ].*
jobX.out1
jobX.out2
[rest of the matches]

% rm !$

Cela permet une inspection de l'expansion des caractères génériques, puis l'utilisation du même modèle de glob exact sans la possibilité d'insérer un espace avant le *.

Je suggère également aux gens de ne jamais couper et coller un motif générique de globalisation sur une ligne de commande potentiellement destructrice. Parce que dans mes propres mains ça a été un problème :)

Un technicien de mon laboratoire vient de faire cette erreur la semaine dernière (3 mois de travail) - et oui nous avons eu une sauvegarde (1 jour de retard).


0

Tout le monde semble dire: "Soyez plus prudent", "Ne faites pas d'invite / confirmation parce que vous vous habituerez à ne pas y prêter une attention appropriée".

Cool, et tout. Je veux dire, je ne pense pas que vous devriez faire un alias pour rm(ni rmrf, puisque vous pouvez facilement le bousiller et taper la vraie commande).

Mais pourquoi ne pouvez-vous pas créer un alias / script et l'appeler, par exemple, removeet ne lui donner qu'un seul argument (par exemple $ 1)? Le caractère générique devrait être de 2 $, en raison de l'espace par inadvertance (amiright?) Et donc votre script / wrapper / alias ne sera pas alimenté le second. Oui, vous ne pouvez effectuer qu'un seul ensemble de suppressions (avec un caractère générique) à la fois, mais c'est le prix que vous paieriez.

Si j'écrivais quelque chose de bien, je pourrais peut-être qu'il me dise le nombre de fichiers et de répertoires qu'il envisage de supprimer, et la taille totale des choses que je supprimais, puis demande une confirmation, mais cela pourrait entraver votre flux. Peut-être en faire une option de drapeau remove? (-je). Vous pouvez également souhaiter vérifier $ 1 pour voir s'il ne s'agit que d' un seul caractère générique et demander une confirmation, et répertorier le répertoire dans lequel vous vous trouvez réellement.


Soit dit en passant, il existe un certain nombre de rmremplaçants. Beaucoup essaient de se conformer à la corbeille de bureau de l'interface utilisateur. Certains de ces éléments méritent d'être étudiés.


6
Votre alias "supprimer" ne supprimera jamais plus d'un fichier à la fois. Vous ne pouvez pas utiliser de caractère générique, car le shell le développe avant que la commande ne le voit.
hymie

Faites-en une évaluation, puis insérez-la dans le script?
user3082

Vous voulez donc taper remove \*? Si votre script laisse le shell élargir son entrée, je ne pense pas qu'il aide beaucoup.
Peter Cordes

0

Une astuce très simple consiste à mettre -rfà la fin: rm whatever* -rf
Cela réduit considérablement le taux d'erreur, car vous tapez plus de caractères après le *, vous avez donc plus de temps pour voir les fautes de frappe.
Cela ne résout pas tout. Juste un truc simple au quotidien.

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.