J'ai donc supprimé mon dossier personnel (ou plus précisément tous les fichiers auxquels j'avais accès en écriture). Qu'est-ce qui s'est passé, c'est que j'avais
build="build"
...
rm -rf "${build}/"*
...
<do other things with $build>
dans un script bash et, après ne plus en avoir besoin $build
, supprimer la déclaration et tous ses usages - sauf le rm
. Bash s'étend joyeusement à rm -rf /*
. Oui.
Je me suis senti stupide, installé la sauvegarde, refait le travail que j'ai perdu. Essayer de dépasser la honte.
Maintenant, je me demande: quelles sont les techniques pour écrire des scripts bash afin que de telles erreurs ne puissent pas se produire, ou du moins soient moins probables? Par exemple, avais-je écrit
FileUtils.rm_rf("#{build}/*")
dans un script en ruby, l'interprète se serait plaint de build
ne pas avoir été déclaré, donc le langage me protège.
Ce que j’ai envisagé lors de bash, outre le corraling rm
(qui, comme le soulignent de nombreuses réponses à des questions connexes, n’est pas sans poser de problèmes):
rm -rf "./${build}/"*
Cela aurait tué mon travail actuel (un dépôt Git) mais rien d’autre.- Une variante / paramétrage de
rm
cette opération nécessite une interaction lorsque vous agissez en dehors du répertoire en cours. (Impossible d'en trouver.) Effet similaire.
Est-ce le cas ou existe-t-il d'autres moyens d'écrire des scripts bash "robustes" en ce sens?
la source
rm -rf "${build}/*
ne pas savoir où vont les guillemets.rm -rf "${build}
fera la même chose à cause de laf
.set -u
n'est pas aussi mal vu qu'il l'set -e
est, mais il a toujours ses pièges.#! /usr/bin/env ruby
en haut de chaque script shell et oublier bash;)Réponses:
ou
Cela ferait en sorte que le shell actuel traite les développements de variables non définies comme une erreur:
set -u
etset -o nounset
sont des options de shell POSIX .Une valeur vide ne déclencherait cependant pas d'erreur.
Pour cela, utilisez
L'expansion de
${variable:?word}
serait étendue à la valeur devariable
sauf si elle est vide ou non définie. S'il est vide ou non défini, l'icôneword
serait affichée en cas d'erreur standard et le shell traiterait l'extension comme une erreur (la commande ne serait pas exécutée et, si elle était exécutée dans un shell non interactif, elle se terminerait). Si vous laissez la:
sortie, l'erreur ne serait déclenchée que pour une valeur non définie, tout comme sousset -u
.${variable:?word}
est une extension de paramètre POSIX .Ni l'un ni l'autre ne provoquerait la fermeture d'un shell interactif à moins que
set -e
(ouset -o errexit
) ne soit également en vigueur.${variable:?word}
provoque la fermeture des scripts si la variable est vide ou non définie.set -u
provoquerait la fermeture d'un script s'il était utilisé avecset -e
.Quant à votre deuxième question. Il n'y a aucun moyen de limiter
rm
pour ne pas travailler en dehors du répertoire en cours.L’implémentation GNU de
rm
a une--one-file-system
option qui l’empêche de supprimer de manière récursive les systèmes de fichiers montés, mais je pense que c’est aussi proche que nous pouvons obtenir sans insérer l’rm
appel dans une fonction qui vérifie réellement les arguments.Comme note latérale:
${build}
est exactement équivalent à$build
moins que le développement ne se produise en tant que partie d’une chaîne où le caractère immédiatement suivant est un caractère valide dans un nom de variable, tel que dans"${build}x"
.la source
${build:?msg}
ou${build?msg}
? 2) Dans le contexte de quelque chose comme les scripts de compilation, je pense qu’utiliser un outil différent de rm pour une suppression plus sûre serait bien: nous savons que nous ne voulons pas travailler en dehors du répertoire actuel, nous le rendons donc explicite en utilisant un accès restreint. commander. Il n'est pas nécessaire de rendre les entreprises plus sûres en général.:
(cela ne déclencherait l'erreur que pour les variables non définies ). Je n'ose pas essayer d'écrire un outil permettant de supprimer des fichiers par un chemin spécifique exclusivement, en toutes circonstances. Analyser des chemins et se soucier / ne pas se soucier de liens symboliques, etc. est un peu trop délicat pour moi pour le moment.Je vais proposer des contrôles de validation normales en utilisant
test
/[ ]
Vous auriez été en sécurité si vous aviez écrit votre script en tant que tel:
Les
[ -n "${build}" ]
vérifications qui"${build}"
sont une chaîne de longueur non nulle .Le
||
est l'opérateur OU logique en bash. Une autre commande sera exécutée si la première a échoué.De cette façon, avait
${build}
été vide / indéfini / etc. le script aurait quitté (avec un code de retour de 1, qui est une erreur générique).Cela vous aurait également protégé au cas où vous auriez supprimé toutes les utilisations,
${build}
car ce[ -n "" ]
sera toujours faux.L'avantage d'utiliser
test
/[ ]
est qu'il y a beaucoup d'autres contrôles plus significatifs qu'il peut également utiliser.Par exemple:
la source
${variable:?word}
propose @Kusalananda?test
(c'est-à-dire[
) a de nombreux autres contrôles pertinents, tels que-d
(l'argument est un répertoire),-f
(l'argument est un fichier),-O
(le fichier appartient à l'utilisateur actuel) et ainsi de suite.${build:?word}
avec elle? Le${variable:?word}
format ne vous protège pas si vous supprimez toutes les occurrences de la variable.if
n’est pas le cas pour tous. 2) "N'auriez-vous pas également supprimé $ {build:? Word} avec elle" - le scénario est que j'ai manqué une utilisation de la variable. L'utilisation${v:?w}
m'aurait protégé des dommages. Si j'avais supprimé tous les usages, même un simple accès n'aurait évidemment pas été préjudiciable.Dans votre cas particulier, j'ai déjà retravaillé le terme "suppression" pour déplacer des fichiers / répertoires (en supposant que / tmp se trouve sur la même partition que votre répertoire):
En coulisse, cela déplace les références de fichier /
$trashdir
répertoire de niveau supérieur des structures de répertoires source vers la destination, sur la même partition, sans perdre de temps à parcourir la structure de répertoires et à libérer les blocs de disque par fichier immédiatement. Cela produit un nettoyage beaucoup plus rapide lorsque le système est en cours d'utilisation active, en contrepartie d'un redémarrage légèrement plus lent (/ tmp est nettoyé au redémarrage).Sinon, une entrée cron pour nettoyer périodiquement /tmp/.trash-$USER empêchera / tmp de se remplir, pour les processus (par exemple, les constructions) qui utilisent beaucoup d’espace disque. Si votre répertoire se trouve sur une autre partition que / tmp, vous pouvez créer un répertoire similaire à / tmp sur votre partition et laisser cron le nettoyer.
Mais surtout, si vous bousillez les variables de quelque manière que ce soit, vous pouvez récupérer le contenu avant le nettoyage.
la source
/tmp
vous trouvez sur une autre partition (je pense que cela l'est toujours pour moi, du moins pour les scripts exécutés dans l'espace utilisateur); ensuite, la corbeille doit être changée (et ne profitera pas de la gestion du système d'exploitation/tmp
).mktemp -d
pour obtenir ce répertoire temporaire sans marcher sur les pieds d'un autre processus (et pour le respecter correctement$TMPDIR
).Utilisez la substitution bash paramater pour attribuer des valeurs par défaut lorsque les variables ne sont pas initialisées, par exemple:
rm -rf $ {variable: - "/ nonexistent"}
la source
J'essaie toujours de démarrer mes scripts Bash avec une ligne
#!/bin/bash -ue
.-e
des moyens « échouent sur la première uncathed e rror »;-u
des moyens « échouent sur la première utilisation de u ndeclared variable ».Trouvez plus de détails dans un article de qualité Utilisez le mode strict non officiel de Bash (sauf si vous aimez le débogage) . Aussi l'auteur recommande d'utiliser,
set -o pipefail; IFS=$'\n\t'
mais pour mes fins, c'est exagéré.la source
Le conseil général pour vérifier si votre variable est définie est un outil utile pour éviter ce genre de problème. Mais dans ce cas, il y avait une solution plus simple.
Il n’est probablement pas nécessaire de déplacer le contenu du
$build
répertoire pour les supprimer, mais pas le$build
répertoire lui-même. Donc, si vous ignorez ce qui est superflu,*
alors une valeur non définie se transformera enrm -rf /
laquelle, par défaut, la plupart des implémentations de rm de la dernière décennie refuseront d’être exécutées (sauf si vous désactivez cette protection avec l’--no-preserve-root
option de GNU rm ).Sauter la fin
/
égalementrm ''
entraînerait le message d'erreur suivant:Cela fonctionne même si votre commande rm n'implémente pas de protection pour
/
.la source
Vous pensez en termes de langage de programmation, mais bash est un langage de script :-) Utilisez donc un paradigme d’ instruction totalement différent pour un paradigme de langage totalement différent .
Dans ce cas:
Étant donné
rmdir
que refusera de supprimer un répertoire non vide, vous devez d'abord supprimer les membres. Vous savez ce que sont ces membres, non? Ce sont probablement des paramètres de votre script, ou dérivés d'un paramètre, donc:Maintenant, si vous mettez d’autres fichiers ou répertoires dedans comme un fichier temporaire ou quelque chose que vous n’auriez pas dû, le
rmdir
jettera une erreur. Manipulez-le correctement, puis:Cette façon de penser m'a bien servi parce que ... oui, j'ai été là.
la source
make clean
utilisera des caractères génériques (sauf si un compilateur très diligent crée une liste de tous les fichiers qu'il crée). De plus, il me semble qu'avoirrm -rf ${build}/${parameter}
seulement fait un peu baisser le problème.make
dans la question. Ecrire une réponse quimake
serait uniquement applicable serait une hypothèse très spécifique et surprenante. Ma réponse ne fonctionne que dans des cas autres quemake
le développement logiciel, autres que votre problème novice spécifique que vous avez rencontré en supprimant vos données.