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?

Scott C Wilson
la source
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
Mat
la source
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.

Kevin
la source
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.

abought
la source
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
Tim Keating
la source
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()
wkeithvan
la source
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.

jotacor
la source
7

La réponse de Matt est la bonne pour les anciens Pythons et Kevin pour les plus récents.

Si vous ne souhaitez pas copier la fonction pour silentremove, cette fonctionnalité est exposée dans path.py en tant que remove_p :

from path import Path
Path(filename).remove_p()
Jason R. Coombs
la source
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".

DevonMcC
la source
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
2

Dans Python 3.4 ou version ultérieure, la méthode pythonique serait:

import os
from contextlib import suppress

with suppress(OSError):
    os.remove(filename)
Ross Castroverde
la source
3
Cela ne diffère pas substantiellement de la réponse proposée ici .
chb
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")
Andy Jones
la source
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")
Baz
la source
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))
Kian
la source
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)
Rishi Bansal
la source
-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]
alper
la source
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