2009-06-14 11 views
4

Cette question est similaire à What is the safest way to empty a directory in *nix?Safe rm fonction -rf en script shell

J'écris script bash qui définit plusieurs constantes de chemin et les utilisera pour le fichier et la manipulation de répertoire (copier, renommer et supprimer). Souvent, il sera nécessaire de faire quelque chose comme:

rm -rf "/${PATH1}" 
rm -rf "${PATH2}/"* 

Tout en développant ce script je veux me protéger des noms comme mistyping PATH1 et CHEMIN2 et éviter les situations où ils sont étendus à une chaîne vide, entraînant ainsi l'essuyage disque entier. J'ai décidé de créer emballage spécial:

rmrf() { 
    if [[ $1 =~ "regex" ]]; then 
     echo "Ignoring possibly unsafe path ${1}" 
     exit 1 
    fi 

    shopt -s dotglob 
    rm -rf -- $1 
    shopt -u dotglob 
} 

qui sera appelé:

rmrf "/${PATH1}" 
rmrf "${PATH2}/"* 

Regex (ou l'expression sed) devrait prendre des chemins comme "*", "/ *", «/**/"," /// * "etc. mais autorise des chemins comme" dir ","/dir ","/dir1/dir2/","/dir1/dir2/* ". Aussi je ne sais pas comment activer la globalisation de shell dans le cas comme "/ dir avec espace/*". Des idées?

EDIT: voilà ce que je suis venu avec à ce jour:

rmrf() { 
    local RES 
    local RMPATH="${1}" 
    SAFE=$(echo "${RMPATH}" | sed -r 's:^((\.?\*+/+)+.*|(/+\.?\*+)+.*|[\.\*/]+|.*/\.\*+)$::g') 
    if [ -z "${SAFE}" ]; then 
     echo "ERROR! Unsafe deletion of ${RMPATH}" 
     return 1 
    fi 

    shopt -s dotglob 
    if [ '*' == "${RMPATH: -1}" ]; then 
     echo rm -rf -- "${RMPATH/%\*/}"* 
     RES=$? 
    else 
     echo rm -rf -- "${RMPATH}" 
     RES=$? 
    fi 
    shopt -u dotglob 

    return $RES 
} 

Utilisation prévue est (note un astérisque intérieur guillemets):

rmrf "${SOMEPATH}" 
rmrf "${SOMEPATH}/*" 

où somepath $ a est système ou/home (dans mon cas, toutes ces opérations sont effectuées sur un système de fichiers monté sous le répertoire/scratch).

: CAVEATS

  • pas testé très bien
  • pas l'intention d'utiliser des chemins pouvant contenir '..' ou '' Rm -rf avec astérisque peut probablement échouer s'il y a trop de fichiers ou de répertoires dans $ SOMEPATH (en raison de la longueur limitée de la ligne de commande) - cela peut être corrigé avec 'pour 'commande loop ou' find '

Répondre

2

Je pense que la commande "rm" a un paramètre pour éviter la suppression de "/". Vérifiez-le.

+0

Merci! Je ne savais pas à propos de cette option. En fait, il s'appelle --preserve-root et n'est pas mentionné dans la page de manuel. – Max

+0

Sur mon système, cette option est activée par défaut, mais elle ne sert à rien dans le cas de rm -ri/* – Max

2

Je vous recommande d'utiliser directement realpath (1) et non directement l'argument de commande, afin d'éviter des éléments comme /A/B/../ ou des liens symboliques.

+0

Commande utile mais non standard. J'ai trouvé un remplacement bash possible: http://www.archlinux.org/pipermail/pacman-dev/2009-February/008130.html – Max

1

Généralement, lorsque je développe une commande avec des opérations telles que 'rm -fr', je vais neutraliser la suppression pendant le développement. Une façon de le faire est:

RMRF="echo rm -rf" 
... 
$RMRF "/${PATH1}" 

Ceci me montre ce qui devrait être supprimé - mais ne le supprime pas. Je vais faire un nettoyage manuel pendant que les choses sont en développement - c'est un petit prix à payer pour ne pas courir le risque de tout gâcher.

La notation '"/${PATH1}"' est un peu inhabituelle; normalement, vous devez vous assurer que PATH1 contient simplement un chemin d'accès absolu.

L'utilisation du métacaractère avec '"${PATH2}/"*' est imprudente et inutile. La seule différence entre l'utilisation de ce paramètre et l'utilisation de '"${PATH2}"' est que si le répertoire spécifié par PATH2 contient des fichiers ou des répertoires dont le nom commence par dot, ces fichiers ou répertoires ne seront pas supprimés. Une telle conception est peu probable et est plutôt fragile. Il serait beaucoup plus simple de simplement passer PATH2 et laisser la suppression récursive faire son travail. L'ajout de la barre oblique n'est pas nécessairement une mauvaise idée. le système devra s'assurer que $PATH2 contient un nom de répertoire, pas seulement un nom de fichier, mais la protection supplémentaire est plutôt minime.

L'utilisation de globbing avec 'rm -fr' est généralement une mauvaise idée. Vous voulez être précis et restrictif et limitatif dans ce qu'il fait - pour éviter les accidents. Bien sûr, vous n'exécuteriez jamais la commande (script shell que vous développez) en tant que root pendant qu'elle est en cours de développement - ce serait suicidaire. Ou, si les privilèges root sont absolument nécessaires, vous neutralisez l'opération de suppression jusqu'à ce que vous soyez certain qu'elle est à l'épreuve des balles.

+0

Pour supprimer les sous-répertoires et les fichiers commençant par un point, j'utilise "shopt -s dotglob". Utiliser rm -rf "$ {PATH2}" n'est pas approprié parce que dans mon cas PATH2 peut seulement être enlevé par le super-utilisateur et cela donne un statut d'erreur pour la commande "rm" (et je vérifie pour suivre d'autres erreurs). – Max

+0

Ensuite, avec tout le respect, vous devez utiliser un sous-répertoire privé sous $ PATH2 que vous pouvez supprimer. Évitez l'expansion de glob avec des commandes comme 'rm -rf' comme vous éviteriez la peste (ou devrait-il être A/H1N1?). –

7

J'ai trouvé un gros danger avec rm dans bash car bash ne s'arrête généralement pas pour des erreurs. Cela signifie que:

cd $SOMEPATH 
rm -rf * 

est une combinaison très dangereuse si le répertoire change échoue. Une façon plus sûre serait:

cd $SOMEPATH && rm -rf * 

qui assurera la haute fréquence ne fonctionnera que si vous êtes vraiment en $ somepath. Cela ne vous protège pas d'un mauvais $ SOMEPATH mais il peut être combiné avec les conseils donnés par d'autres pour rendre votre script plus sûr.

+0

belle astuce, je suis une victime stupide. –

1

S'il est possible, vous devriez essayer de tout mettre dans un dossier avec un nom codé en dur qui est peu susceptible d'être trouvé nulle part ailleurs sur le système de fichiers, tel que 'foofolder'. Ensuite, vous pouvez écrire votre fonction rmrf() comme:

rmrf() { 
    rm -rf "foofolder/$PATH1" 
    # or 
    rm -rf "$PATH1/foofolder" 
} 

Il n'y a aucun moyen que la fonction peut supprimer quoi que ce soit, mais les fichiers que vous le souhaitez.

+0

En fait il y a un moyen: si 'PATH1' est quelque chose comme' ../../ someotherdir' – vadipp

1

Vous pouvez utiliser

set -f # cf. help set 

pour désactiver la génération de nom de fichier (*).

4

Il existe une directive bash set -u qui provoquera l'exit, lorsqu'une variable non initialisée est utilisée. J'ai lu à ce sujet here, avec rm -rf à titre d'exemple. Je pense que c'est ce que vous cherchez. Et voici set's manual.

1

Vous n'avez pas besoin d'utiliser des expressions régulières.
Attribuez simplement les répertoires que vous souhaitez protéger à une variable, puis passez en revue la variable. par exemple:

 
protected_dirs="/ /bin /usr/bin /home $HOME" 
for d in $protected_dirs; do 
    if [ "$1" = "$d" ]; then 
     rm=0 
     break; 
    fi 
done 
if [ ${rm:-1} -eq 1 ]; then 
    rm -rf $1 
fi 
0

Ajouter les codes suivants à votre ~/.bashrc

# safe delete 
move_to_trash() { now="$(date +%Y%m%d_%H%M%S)"; mv "[email protected]" ~/.local/share/Trash/files/"[email protected]_$now"; } 
alias del='move_to_trash' 

# safe rm 
alias rmi='rm -i' 

Chaque fois que vous devez rm quelque chose, tout d'abord considérer del, vous pouvez modifier le dossier poubelle.Si vous avez besoin de quelque chose, vous pouvez aller dans le dossier Corbeille et utiliser rmi.