Il n’y a pas de corbeille en SSH. Sur un serveur, rm ne déplace rien : il retire l’entrée du répertoire et marque les blocs comme réutilisables. Aucune confirmation, aucun historique, aucun « annuler ». La seule chose qui vous sauve, c’est une sauvegarde antérieure — ou le fait de ne pas avoir lancé la commande.
Ce guide part de cette contrainte. Il donne les commandes utiles, mais surtout ce qui les entoure : comment mesurer avant d’agir, pourquoi rm * échoue passé un certain nombre de fichiers, quelle méthode est réellement la plus rapide sur cent mille fichiers, et pourquoi l’espace disque ne revient parfois pas après une suppression réussie. Les chiffres qui suivent ont été mesurés, pas estimés.
Les six commandes qui couvrent presque tous les cas
Avant les subtilités, l’essentiel. Ces six formes suffisent à traiter la très grande majorité des suppressions sur un serveur web.
| Commande | Effet | Quand l’employer |
|---|---|---|
rm fichier.log | Supprime un fichier. Échoue si c’est un dossier. | Cas unitaire, le plus sûr. |
rm -i *.log | Demande confirmation pour chaque fichier. | Quand le motif vous semble juste sans en être certain. |
rmdir cache | Supprime un dossier uniquement s’il est vide. | Filet de sécurité : refuse d’agir si le dossier contient quelque chose. |
rm -r cache | Supprime un dossier et son contenu, en demandant pour les fichiers protégés. | Suppression récursive avec un minimum de garde-fou. |
rm -rf cache | Supprime sans question et sans erreur si la cible n’existe pas. | Scripts et automatisation. Jamais tapé à la main sur un chemin long. |
find . -name "*.tmp" -delete | Supprime par critère, sans passer par l’interpréteur de commandes. | Dès que la sélection dépasse ce qu’un motif simple exprime. |
Une remarque sur -f. Beaucoup de tutoriels l’ajoutent par réflexe. Il fait deux choses : il supprime les fichiers en lecture seule sans demander, et il tait les erreurs. Cette seconde partie est la dangereuse. rm -rf /var/www/site/uplaods — avec la faute de frappe — ne dit rien et renvoie un code de succès. Sans -f, la même commande aurait affiché « aucun fichier ou dossier de ce nom », et vous auriez relu votre chemin.
Mesurer avant de supprimer
La plupart des suppressions catastrophiques commencent par une mauvaise estimation de ce qu’on croit supprimer. Trois commandes évitent cela, et elles coûtent quelques secondes.
| Question | Commande | Ce que vous apprenez |
|---|---|---|
| Qu’est-ce qui pèse lourd ici ? | du -sh * | sort -h | tail -20 | Les vingt entrées les plus volumineuses du dossier courant, triées. |
| Combien de fichiers vais-je toucher ? | find . -name "*.log" | wc -l | Le compte exact, avant de remplacer | wc -l par -delete. |
| Lesquels exactement ? | find . -name "*.log" -printf "%TY-%Tm-%Td %10s %p\n" | sort | head -40 | Date, taille et chemin des premiers concernés. |
| Quel espace vais-je récupérer ? | find . -name "*.log" -print0 | du --files0-from=- -ch | tail -1 | Le total réel des fichiers sélectionnés. |
| Y a-t-il un lien symbolique ? | find . -maxdepth 2 -type l -ls | Un rm -rf sur un lien vers un montage partagé détruit la cible. |
La règle qui en découle tient en une phrase : toute commande de suppression s’écrit d’abord sans le verbe de suppression. On remplace -delete par -print, ou rm par echo rm. On lit la sortie. Puis on rappelle la ligne et on remplace. Ce détour coûte dix secondes et il a sauvé plus de sites que n’importe quelle sauvegarde.
Supprimer par critère : ce que find apporte
Un motif comme *.log ne dit rien de l’âge, de la taille ni de la profondeur. find exprime ces trois dimensions, et il les combine.
| Besoin | Commande |
|---|---|
| Journaux de plus de trente jours | find /var/log/site -name "*.log" -mtime +30 -delete |
| Fichiers de plus de 100 Mo | find . -type f -size +100M -printf "%10s %p\n" | sort -rn |
| Cache du jour uniquement | find var/cache -mindepth 1 -maxdepth 1 -mtime 0 -exec rm -rf {} + |
| Dossiers vides, en remontant | find . -depth -type d -empty -delete |
| Tout sauf un dossier | find . -mindepth 1 -maxdepth 1 ! -name "uploads" -exec rm -rf {} + |
| Sans franchir un point de montage | find . -xdev -name "*.tmp" -delete |
Deux détails techniques méritent d’être compris plutôt que copiés.
-depth impose à find de traiter le contenu d’un dossier avant le dossier lui-même. Sans lui, -type d -empty -delete ne supprime qu’un niveau : le dossier parent n’était pas vide au moment où find l’a examiné. Avec lui, une arborescence entière de dossiers vides disparaît en une passe.
-exec rm -rf {} + et -exec rm -rf {} \; ne sont pas équivalents. Le point-virgule lance un processus rm par fichier trouvé ; le signe plus regroupe autant de chemins que la limite du système l’autorise et lance beaucoup moins de processus. Sur dix mille fichiers, l’écart se compte en dizaines de secondes.
La limite que personne n’anticipe : rm * sur des milliers de fichiers
Un jour, sur un dossier de cache qui a grossi, cette commande répond :
$ rm -f *
-bash: /bin/rm: Argument list too long
Ce n’est pas rm qui refuse. C’est le noyau. L’interpréteur de commandes développe * en la liste complète des noms de fichiers et la passe au programme ; cette liste ne peut pas dépasser une taille fixée par le système. Sur la machine où ce guide a été écrit :
$ getconf ARG_MAX
2097152
Soit deux mégaoctets pour l’ensemble des arguments et des variables d’environnement. En pratique, avec des noms de fichiers d’une douzaine de caractères, la rupture a été observée entre cinquante mille et cent mille arguments : cinquante mille passaient, cent mille déclenchaient l’erreur. Le seuil exact dépend de la longueur des noms et de la taille de votre environnement — ce qui explique pourquoi la même commande passe sur un serveur et échoue sur un autre.
Trois contournements fonctionnent, et aucun ne consiste à augmenter la limite.
| Méthode | Commande | Pourquoi ça marche |
|---|---|---|
| Supprimer le dossier entier | rm -rf cache && mkdir cache | Un seul argument passé à rm. Attention aux droits et au propriétaire à recréer. |
find avec -delete | find cache -mindepth 1 -delete | find appelle le noyau fichier par fichier, sans liste d’arguments. |
xargs par lots | find cache -type f -print0 | xargs -0 -r rm -f | xargs découpe lui-même en lots sous la limite. |
Le -print0 associé à -0 n’est pas décoratif : il sépare les chemins par un octet nul au lieu d’un saut de ligne, seul moyen fiable de traiter des noms contenant des espaces, des apostrophes ou des retours à la ligne. Le -r évite que xargs lance rm sans argument quand la recherche ne trouve rien.
Quatre méthodes, mesurées
La question « laquelle est la plus rapide » revient souvent, et les réponses trouvées en ligne sont rarement chiffrées. Voici une mesure. Protocole : une arborescence de fichiers de 64 octets répartis par lots de deux cents dans des sous-dossiers à deux niveaux, recréée avant chaque essai, sur le même système de fichiers, machine au repos.
| Méthode | 10 000 fichiers | 50 000 fichiers | 100 000 fichiers |
|---|---|---|---|
rm -rf | 0,13 s | 0,47 s | 0,98 s |
find -delete | 0,10 s | 0,48 s | 1,01 s |
find | xargs rm | 0,13 s | 0,58 s | 1,17 s |
Perl unlink | 0,18 s | 0,74 s | 1,71 s |
Trois conclusions se dégagent, et la première est la plus utile : entre les trois méthodes du système, l’écart ne dépasse pas vingt pour cent. Choisir find -delete plutôt que rm -rf pour gagner du temps ne se justifie pas ; on choisit find parce qu’il exprime un critère, pas parce qu’il va plus vite.
Deuxième conclusion : les trois croissent linéairement avec le nombre de fichiers. Le coût est dominé par les appels système d’un déréférencement par fichier, pas par le volume de données. Supprimer cent mille fichiers de 64 octets prend le même temps que cent mille fichiers de plusieurs mégaoctets — ce qui explique pourquoi vider un dossier de vignettes est lent alors qu’il ne pèse presque rien.
Troisième conclusion : passer par un langage de script coûte cher. Perl est soixante-quinze pour cent plus lent à cent mille fichiers, parce qu’il parcourt l’arborescence en mémoire avant de supprimer. Une version en Python ou en PHP se comporterait de la même façon. Sur un serveur, la commande native reste le bon outil.
Un mot d’honnêteté sur ces chiffres : ils ont été relevés sur un système de fichiers rapide, machine peu chargée. Sur un disque mécanique, un NFS ou un hébergement mutualisé chargé, les valeurs absolues peuvent être dix à cent fois plus élevées. Ce qui reste valable, ce sont les rapports entre méthodes et la linéarité, pas les durées elles-mêmes. Une seule commande a échappé à la mesure : rsync --delete, absente de la machine d’essai. Mieux vaut le dire que produire un chiffre inventé.
Quand l’espace disque ne revient pas
Voici un scénario qui coûte des heures de recherche. Vous supprimez un journal de plusieurs gigaoctets qui remplissait le disque. ls confirme qu’il a disparu. df affiche toujours le disque plein.
Ce n’est pas un bug, et il n’y a rien à réparer. Sous Linux, un fichier ne disparaît réellement que lorsque deux conditions sont réunies : plus aucune entrée de répertoire n’y mène, et plus aucun processus ne le tient ouvert. Un serveur web, une base de données ou un service de journalisation qui écrivait dans ce fichier en garde le descripteur. Le fichier devient invisible mais ses blocs restent réservés.
La démonstration, mesurée :
espace libre avant : 27 538 Mo
après rm, descripteur toujours ouvert : 27 538 Mo (le fichier n'existe plus)
après fermeture du descripteur : 27 658 Mo
espace rendu à la fermeture : 120 Mo
Le fichier pesait cent vingt mégaoctets. La suppression n’a rien rendu. La fermeture du descripteur a tout rendu d’un coup. Pour identifier le coupable sur un serveur :
$ lsof +L1
COMMAND PID USER FD TYPE SIZE/OFF NLINK NODE NAME
nginx 1247 www 12w REG 2147483648 0 393218 /var/log/nginx/access.log (deleted)
La colonne NLINK à zéro est la signature : plus aucun lien, mais un descripteur ouvert. Deux issues possibles. La propre consiste à demander au service de rouvrir ses journaux — systemctl reload nginx, ou le signal que documente le service. La brutale consiste à redémarrer le processus. Et la solution qui évite le problème consiste à ne pas supprimer un journal actif : on le vide sans le supprimer.
# vide le fichier sans toucher au descripteur
$ : > /var/log/nginx/access.log
# ou, équivalent et plus explicite
$ truncate -s 0 /var/log/nginx/access.log
L’espace est rendu immédiatement, le service continue d’écrire dans le même fichier, aucun redémarrage n’est nécessaire. C’est la manière correcte de traiter un journal qui remplit un disque en production.
Les cinq suppressions qui cassent un site
Ces cinq cas reviennent dans les incidents que nous reprenons. Aucun n’est théorique.
1. L’espace involontaire après la barre oblique
rm -rf /var/www/ site au lieu de rm -rf /var/www/site. L’interpréteur voit deux cibles : le dossier complet, puis un dossier site relatif. La protection --preserve-root, active par défaut sur les distributions courantes, ne couvre que / exactement — pas /var/www/. La seule défense est la relecture avant validation.
2. La variable vide dans un script
rm -rf "$DOSSIER_CIBLE/"* # si la variable est vide : rm -rf /*
Un script qui n’a pas trouvé sa variable d’environnement supprime la racine. Deux lignes l’évitent, et elles devraient ouvrir tout script de déploiement :
set -euo pipefail
: "${DOSSIER_CIBLE:?la variable DOSSIER_CIBLE doit être définie}"
Le set -u fait échouer le script sur toute variable non définie ; la seconde ligne produit un message explicite plutôt qu’un chemin vide.
3. Le lien symbolique pris pour un dossier
rm -rf uploads quand uploads est un lien vers un stockage partagé monté ailleurs. La commande suit le lien et vide le stockage — y compris pour les autres sites qui l’utilisent. find . -maxdepth 2 -type l -ls avant toute suppression récursive répond à la question.
4. Le fichier caché oublié
rm -rf site/* laisse intacts .env, .htaccess, .git et .well-known, parce que le motif * ne correspond pas aux noms commençant par un point. On croit avoir vidé le dossier ; les secrets de connexion à la base y sont toujours. C’est un problème de sécurité autant que de propreté.
5. Le cache supprimé avec ses droits
rm -rf var/cache && mkdir var/cache recrée le dossier avec le propriétaire et les droits de l’utilisateur connecté, pas ceux du serveur web. Le site répond alors une erreur cinq cents sans que rien n’apparaisse dans les journaux applicatifs. mkdir -p var/cache && chown www-data:www-data var/cache ferme le cas — après avoir relevé le propriétaire d’origine avec stat -c "%U:%G %a".
Vider un dossier sans le supprimer
C’est le besoin réel derrière la plupart des suppressions sur un site : vider un cache, des sessions, des exports temporaires, sans perdre le dossier ni ses droits.
| Commande | Fichiers cachés | Droits du dossier |
|---|---|---|
rm -rf dossier/* | Non traités | Conservés |
rm -rf dossier/{*,.*} | Traités, mais génère des erreurs sur . et .. | Conservés |
find dossier -mindepth 1 -delete | Traités proprement | Conservés |
rm -rf dossier && mkdir dossier | Traités | Perdus, à rétablir |
La troisième forme est celle à retenir. -mindepth 1 exclut le dossier lui-même du résultat : son contenu part, lui reste, avec son propriétaire et ses permissions intacts. C’est la commande que nous mettons dans les scripts de purge de cache.
Rendre une suppression réversible
La meilleure défense n’est pas une commande plus prudente, c’est de ne pas supprimer tout de suite. Trois pratiques, par ordre d’efficacité.
Déplacer avant de supprimer. mv suspect /tmp/a-supprimer-2026-09-17/ puis, quelques jours plus tard, quand le site tourne toujours, la vraie suppression. Le déplacement dans le même système de fichiers est instantané quel que soit le volume, parce qu’il ne fait que réécrire une entrée de répertoire. Ce délai est la seule chose qui distingue une erreur d’un incident.
Archiver ce qui part. tar -czf /tmp/cache-2026-09-17.tar.gz var/cache && find var/cache -mindepth 1 -delete. L’archive coûte peu sur du cache compressible et se supprime à son tour dans un mois.
Refuser la commande destructive à la main. Dans un fichier de profil : alias rm='rm -I --preserve-root'. Le -I majuscule demande une seule confirmation quand plus de trois fichiers sont concernés — assez peu intrusif pour ne pas être contourné, assez présent pour arrêter une faute de frappe. Les scripts, eux, appellent /bin/rm explicitement et ne sont pas affectés.
Sur un hébergement mutualisé
Une partie de ce guide suppose un accès root. Sur un hébergement mutualisé, plusieurs choses changent. L’accès SSH est parfois restreint à un interpréteur limité, sans find ni lsof. Les processus des autres comptes sont invisibles, donc lsof +L1 ne montrera pas le service qui tient votre journal ouvert. Le redémarrage d’un service n’est pas à votre main. Et la limite d’arguments est la même pour tous, mais le nombre de processus simultanés autorisé est souvent bas, ce qui rend -exec {} \; particulièrement pénalisant.
Dans ce contexte, deux réflexes : préférer find -delete, qui n’ouvre aucun processus supplémentaire, et vider les journaux par truncate plutôt que les supprimer, puisque vous ne pourrez pas recharger le service.
La liste à relire avant toute suppression en production
- Où suis-je ?
pwd. Une commande relative lancée du mauvais dossier est la première cause d’accident. - Qu’est-ce que je vais toucher ? La même commande avec
-printau lieu de-delete, ouechodevantrm. - Combien ?
| wc -l. Un ordre de grandeur inattendu signale un motif trop large. - Des liens symboliques dans le chemin ?
find . -maxdepth 2 -type l -ls. - Le service peut-il tenir un descripteur ? S’il s’agit d’un journal actif,
truncateet nonrm. - Quels droits à rétablir ?
stat -c "%U:%G %a" dossier, noté avant de supprimer le dossier lui-même. - Ai-je une sauvegarde datée d’avant cette commande ? Si la réponse est « je crois », la réponse est non.
- Puis-je déplacer au lieu de supprimer ? Dans le doute,
mv, et la suppression dans huit jours.
Ces huit points prennent moins de deux minutes. Une restauration de sauvegarde en prend plusieurs heures, quand elle est possible — et l’expérience montre qu’elle ne l’est pas toujours, parce que la sauvegarde datait d’après l’incident, ou parce que personne n’avait vérifié qu’elle se restaurait.
C’est d’ailleurs le vrai sujet derrière ce guide. Les commandes s’apprennent en une heure. Ce qui distingue un serveur qu’on peut réparer d’un serveur qu’on doit reconstruire, c’est une maintenance qui vérifie ses sauvegardes et une infogérance qui garde trace de qui a lancé quoi, et quand.


