La façon la plus pythonique de supprimer un fichier qui peut ne pas exister


453

Je souhaite supprimer le fichier filenames'il existe. Est-il approprié de dire

if os.path.exists(filename):
    os.remove(filename)

Y a-t-il une meilleure façon? Un moyen sur une ligne?


7
Voulez-vous essayer de supprimer un fichier s'il existe (et échouer si vous manquez d'autorisations) ou faire une suppression au mieux et ne jamais avoir une erreur renvoyée en face?
Donal Fellows

Je voulais faire "l'ancien" de ce que @DonalFellows a dit. Pour cela, je suppose que le code original de Scott serait une bonne approche?
LarsH

Créez une fonction appelée unlinket placez-la dans l'espace de noms PHP.
lama12345

1
@LarsH Voir le deuxième bloc de code de la réponse acceptée. Il relance l'exception si l'exception est tout sauf une erreur «aucun fichier ou répertoire».
jpmc26

Réponses:


613

Une manière plus pythonique serait:

try:
    os.remove(filename)
except OSError:
    pass

Bien que cela prenne encore plus de lignes et semble très moche, cela évite l'appel inutile os.path.exists()et suit la convention python de surutilisation des exceptions.

Il peut être utile d'écrire une fonction pour ce faire:

import os, errno

def silentremove(filename):
    try:
        os.remove(filename)
    except OSError as e: # this would be "except OSError, e:" before Python 2.6
        if e.errno != errno.ENOENT: # errno.ENOENT = no such file or directory
            raise # re-raise exception if a different error occurred

17
Mais cela se passerait-il si l'opération de suppression échouait (système de fichiers en lecture seule ou autre problème inattendu)?
Scott C Wilson

136
De plus, le fait que le fichier existe lorsqu'il os.path.exists()est exécuté ne signifie pas qu'il existe lorsqu'il os.remove()est exécuté.
2012

8
Mon +1, mais la surutilisation des exceptions n'est pas une convention Python :) Ou est-ce?
pepr

8
@pepr Je critiquais avec humour la façon dont les exceptions font partie du comportement normal en python. Par exemple, les itérateurs doivent lever des exceptions pour arrêter l'itération.
Matt

5
+1 parce que je ne peux pas +2. En plus d'être plus Pythonic, celui-ci est en fait correct, tandis que l'original ne l'est pas, pour la raison que tout le monde suggère. De telles conditions de course mènent à des failles de sécurité, des bugs difficiles à reproduire, etc.
abarnert

160

Je préfère supprimer une exception plutôt que de vérifier l'existence du fichier, pour éviter un bug TOCTTOU . La réponse de Matt en est un bon exemple, mais nous pouvons la simplifier légèrement sous Python 3, en utilisant contextlib.suppress():

import contextlib

with contextlib.suppress(FileNotFoundError):
    os.remove(filename)

Si filename est un pathlib.Pathobjet au lieu d'une chaîne, nous pouvons appeler sa .unlink()méthode au lieu d'utiliser os.remove(). D'après mon expérience, les objets Path sont plus utiles que les chaînes pour la manipulation du système de fichiers.

Étant donné que tout dans cette réponse est exclusif à Python 3, il fournit une autre raison de mettre à niveau.


8
C'est la manière la plus pythonique de décembre 2015. Python continue cependant d'évoluer.
Mayank Jaiswal

2
Je n'ai trouvé aucune méthode remove () pour les objets pathlib.Path sur Python 3.6
BrianHVB

1
@jeffbyrnes: J'appellerais cela une violation du Zen de Python: "Il devrait y avoir une - et de préférence une seule - manière évidente de le faire." Si vous aviez deux méthodes qui faisaient la même chose, vous vous retrouveriez avec un mélange d'entre elles dans l'exécution du code source, ce qui serait plus difficile à suivre pour le lecteur. Je soupçonne qu'ils voulaient une cohérence avec unlink(2), qui est de loin la plus ancienne interface pertinente ici.
Kevin

1
@nivk: Si vous avez besoin d'une exceptclause, vous devez utiliser try/ except. Il ne peut pas être raccourci de manière significative, car vous devez avoir une ligne pour introduire le premier bloc, le bloc lui-même, une ligne pour introduire le deuxième bloc, puis ce bloc, donc try/ exceptest déjà aussi concis que possible.
Kevin

1
Il convient de noter que contrairement à un bloc try / except, cette solution signifie que vous n'avez pas à vous soucier de créer une exception afin de vous assurer que les mesures de couverture de test sont pertinentes.
thclark

50

os.path.existsrenvoie Truepour les dossiers ainsi que les fichiers. Pensez à utiliser os.path.isfilepour vérifier si le fichier existe à la place.


4
Chaque fois que nous testons l'existence et retirons ensuite sur la base de ce test, nous nous ouvrons à une condition de concurrence. (Et si le fichier disparaît entre les deux?)
Alex L

34

Dans l'esprit de la réponse d'Andy Jones, que diriez-vous d'une authentique opération ternaire:

os.remove(fn) if os.path.exists(fn) else None

41
Mauvaise utilisation des ternaires.
bgusach du

19
@BrianHVB Parce que les ternaires sont là pour choisir entre deux valeurs basées sur une condition, pas pour faire des branchements.
bgusach

1
Je n'aime pas utiliser d'exceptions pour le contrôle de flux. Ils rendent le code difficile à comprendre et, plus important encore, peuvent masquer une autre erreur se produisant (comme un problème d'autorisation bloquant la suppression d'un fichier) qui provoquera un échec silencieux.
Ed King

11
Ce n'est pas atomique. Le fichier peut être supprimé entre les appels à existe et supprimer. Il est plus sûr de tenter l'opération et de lui permettre d'échouer.
ConnorWGarvey

1
@ nam-g-vu Juste pour info, j'ai annulé votre modification parce que vous venez d'ajouter la syntaxe du questionneur d'origine comme alternative. Comme ils cherchaient quelque chose de différent, je ne pense pas que la modification soit pertinente pour cette réponse particulière.
Tim Keating

10

Depuis Python 3.8, utilisez missing_ok=Trueet pathlib.Path.unlink( docs ici )

from pathlib import Path

my_file = Path("./dir1/dir2/file.txt")

# Python 3.8+
my_file.unlink(missing_ok=True)

# Python 3.7 and earlier
if my_file.exists():
    my_file.unlink()

1
La meilleure réponse pour python3 pratique à mon avis.
mrgnw

9

Une autre façon de savoir si le fichier (ou les fichiers) existe et de le supprimer est d'utiliser le module glob.

from glob import glob
import os

for filename in glob("*.csv"):
    os.remove(filename)

Glob trouve tous les fichiers qui pourraient sélectionner le modèle avec un caractère générique * nix et boucle la liste.



6
if os.path.exists(filename): os.remove(filename)

est une doublure.

Beaucoup d'entre vous ne sont peut-être pas d'accord - peut-être pour des raisons telles que le fait de considérer l'utilisation proposée des ternaires comme "laide" - mais cela soulève la question de savoir si nous devons écouter les gens habitués à des normes moches lorsqu'ils appellent quelque chose de non standard "laid".


3
c'est propre - je n'aime pas utiliser d'exceptions pour le contrôle de flux. Ils rendent le code difficile à comprendre et, plus important encore, peuvent masquer une autre erreur se produisant (comme un problème d'autorisation bloquant la suppression d'un fichier) qui provoquera un échec silencieux.
Ed King

2
Ce n'est pas joli car cela suppose qu'il n'y a qu'un seul processus qui modifiera le nom de fichier. Ce n'est pas atomique. Il est sûr et correct de tenter l'opération et d'échouer avec élégance. C'est ennuyeux que Python ne puisse pas standardiser. Si nous avions un répertoire, nous utiliserions shutil et il prendrait en charge exactement ce que nous voulons.
ConnorWGarvey


1

Quelque chose comme ça? Profite de l'évaluation des courts-circuits. Si le fichier n'existe pas, le conditionnel entier ne peut pas être vrai, donc python ne dérangera pas l'évaluation de la deuxième partie.

os.path.exists("gogogo.php") and os.remove("gogogo.php")

25
Ce n'est certainement pas "plus Pythonique" - en fait, c'est quelque chose que Guido met en garde spécifiquement, et se réfère à "l'abus" des opérateurs booléens.
abarnert

1
oh, je suis d'accord - une partie de la question demandait un chemin d'une ligne et c'était la première chose qui m'est venue à l'esprit
Andy Jones

4
Eh bien, vous pouvez également en faire une ligne simple en supprimant simplement la nouvelle ligne après les deux points ... Ou, mieux encore, Guide a ajouté à contrecœur l'expression si pour empêcher les gens "d'abuser des opérateurs booléens", et il y a une excellente occasion de prouver que tout peut être utilisé abusivement: os.remove ("gogogo.php") si os.path.exists ("gogogo.php") else Aucun. :)
abarnert

0

Une offre KISS:

def remove_if_exists(filename):
  if os.path.exists(filename):
    os.remove(filename)

Et alors:

remove_if_exists("my.file")

1
Si vous devez écrire une fonction entière, il manque en quelque sorte le but des lignes
simples

@Ion Lesan L'OP est la "meilleure" façon de résoudre ce problème. Un liner n'est jamais un meilleur moyen s'il met en péril la lisibilité.
Baz

Compte tenu de la définition intrinsèquement large de «meilleur», je ne vais pas discuter dans ce sens, bien qu'il soit clairement affecté par TOCTOU. Et certainement pas une solution KISS.
Ion Lesan

@Matt True, mais un certain nombre de solutions proposées ici ne souffrent pas de ce problème?
Baz

0

Voici une autre solution:

if os.path.isfile(os.path.join(path, filename)):
    os.remove(os.path.join(path, filename))

0

Une autre solution avec votre propre message en exception.

import os

try:
    os.remove(filename)
except:
    print("Not able to delete the file %s" % filename)

-1

J'ai utilisé rmce qui peut forcer la suppression de fichiers inexistants avec --preserve-rootcomme option pour rm.

--preserve-root
              do not remove `/' (default)

rm --help | grep "force"
  -f, --force           ignore nonexistent files and arguments, never prompt

Nous pouvons également utiliser safe-rm ( sudo apt-get install safe-rm)

Safe-rm est un outil de sécurité destiné à empêcher la suppression accidentelle de fichiers importants en remplaçant / bin / rm par un wrapper, qui vérifie les arguments donnés par rapport à une liste noire configurable de fichiers et de répertoires qui ne doivent jamais être supprimés.

Je vérifie d'abord si le chemin du dossier / fichier existe ou non. Cela empêchera de définir la variable fileToRemove /folderToRemove to the string-r / `.


import os, subprocess

fileToRemove = '/home/user/fileName';
if os.path.isfile(fileToRemove):
   subprocess.run(['rm', '-f', '--preserve-root', fileToRemove]
   subprocess.run(['safe-rm', '-f', fileToRemove]

1
Utiliser un shell pour quelque chose d'aussi banal est exagéré et cette approche ne fonctionnera pas non plus multiplateforme (c'est-à-dire Windows).
Nabla

4
Utiliser un shell au lieu de la bibliothèque standard (os.remove par exemple) est toujours l'une des façons les moins pythoniques / propres de faire quelque chose. Par exemple, vous devez gérer manuellement les erreurs renvoyées par le shell.
Nabla

1
J'ai ajouté ma réponse pour utiliser en rmtoute sécurité et prévenir rm -r /. @JonBrave
alper

1
rm -f --preserve-rootn'est pas assez bon ( --preserve-rootc'est probablement la valeur par défaut de toute façon). J'ai donné -r / comme exemple , que faire si c'est -r /homeou quoi? Vous voulez probablement rm -f -- $fileToRemove, mais ce n'est pas le but.
JonBrave

3
Pas comme vous l'avez utilisé, avec un nom de variable (variable d'environnement), sans guillemets et sans protection, non. Et pas pour cette question, non. Exposer les imprudents os.system('rm ...')est extrêmement dangereux, désolé.
JonBrave
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.