Die pythonischste Methode zum Löschen einer Datei, die möglicherweise nicht vorhanden ist

453

Ich möchte die Datei löschen, filenamefalls vorhanden. Ist es richtig zu sagen

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

Gibt es einen besseren Weg? Ein einzeiliger Weg?

Scott C Wilson
quelle
7
Möchten Sie versuchen, eine Datei zu löschen, wenn sie vorhanden ist (und fehlschlagen, wenn Sie keine Berechtigungen haben), oder nach besten Kräften löschen, ohne dass Ihnen ein Fehler ins Gesicht zurückgeworfen wird?
Donal Fellows
Ich wollte "das erstere" von dem machen, was @DonalFellows sagte. Dafür wäre Scotts Originalcode wohl ein guter Ansatz?
LarsH
Erstellen Sie eine aufgerufene Funktion unlinkund fügen Sie sie in den Namespace PHP ein.
Lama12345
1
@LarsH Siehe den zweiten Codeblock der akzeptierten Antwort. Die Ausnahme wird erneut ausgelöst, wenn die Ausnahme alles andere als ein Fehler "Keine solche Datei oder kein solches Verzeichnis" ist.
jpmc26

Antworten:

613

Ein pythonischerer Weg wäre:

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

Dies dauert zwar noch mehr Zeilen und sieht sehr hässlich aus, vermeidet jedoch den unnötigen Aufruf os.path.exists()und folgt der Python-Konvention der übermäßigen Verwendung von Ausnahmen.

Es kann sich lohnen, eine Funktion zu schreiben, um dies für Sie zu tun:

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
Matt
quelle
17
Aber würde dies passieren, wenn der Entfernungsvorgang fehlschlägt (schreibgeschütztes Dateisystem oder ein anderes unerwartetes Problem)?
Scott C Wilson
134
Die Tatsache, dass die Datei bei der os.path.exists()Ausführung vorhanden ist, bedeutet nicht, dass sie bei der os.remove()Ausführung vorhanden ist.
Kindall
8
Meine +1, aber übermäßige Verwendung von Ausnahmen ist keine Python-Konvention :) Oder doch?
Pepr
8
@pepr Ich habe nur humorvoll kritisiert, wie Ausnahmen Teil des normalen Verhaltens in Python sind. Zum Beispiel Iteratoren müssen Ausnahmen erhöhen , um das Iterieren zu stoppen.
Matt
5
+1 weil ich nicht +2 kann. Abgesehen davon, dass es mehr pythonisch ist, ist dieses tatsächlich korrekt, während das Original aus dem vorgeschlagenen Grund nicht korrekt ist. Solche Rennbedingungen führen zu Sicherheitslücken, schwer zu
reproduzierenden
159

Ich ziehe es vor, eine Ausnahme zu unterdrücken, anstatt nach der Existenz der Datei zu suchen , um einen TOCTTOU- Fehler zu vermeiden . Matts Antwort ist ein gutes Beispiel dafür, aber wir können sie unter Python 3 leicht vereinfachen, indem wir contextlib.suppress():

import contextlib

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

Wenn filenamees sich um ein pathlib.PathObjekt anstelle eines Strings handelt, können wir dessen .unlink()Methode anstelle von verwenden os.remove(). Nach meiner Erfahrung sind Pfadobjekte für die Manipulation des Dateisystems nützlicher als Zeichenfolgen.

Da alles in dieser Antwort exklusiv für Python 3 ist, bietet es einen weiteren Grund für ein Upgrade.

Kevin
quelle
7
Dies ist der pythonischste Weg wie im Dezember 2015. Python entwickelt sich jedoch weiter.
Mayank Jaiswal
2
Ich habe keine remove () -Methode für pathlib.Path-Objekte in Python 3.6 gefunden
BrianHVB
1
@ Jeffbyrnes: Ich würde das eine Verletzung des Zen von Python nennen: "Es sollte einen - und vorzugsweise nur einen - offensichtlichen Weg geben, dies zu tun." Wenn Sie zwei Methoden hätten, die dasselbe taten, würden Sie eine Mischung davon im laufenden Quellcode erhalten, was für den Leser schwieriger zu befolgen wäre. Ich vermute, sie wollten Konsistenz mit unlink(2), was bei weitem die älteste relevante Schnittstelle hier ist.
Kevin
1
@nivk: Wenn Sie eine exceptKlausel benötigen , sollten Sie try/ verwenden except. Es kann nicht sinnvoll gekürzt werden, da Sie eine Zeile zum Einführen des ersten Blocks, den Block selbst, eine Zeile zum Einführen des zweiten Blocks und dann diesen Block haben müssen, damit try/ exceptbereits so knapp wie möglich ist.
Kevin
1
Es ist erwähnenswert, dass diese Lösung im Gegensatz zu einem Try / Except-Block bedeutet, dass Sie nicht herumspielen müssen, um eine Ausnahme zu erstellen, um sicherzustellen, dass die Messwerte für die Testabdeckung relevant sind.
Park
50

os.path.existsGibt sowohl Truefür Ordner als auch für Dateien zurück. Verwenden Sie os.path.isfile, um zu überprüfen, ob die Datei stattdessen vorhanden ist.

überlegt
quelle
4
Jedes Mal, wenn wir auf Existenz testen und dann basierend auf diesem Test entfernen, öffnen wir uns einer Rennbedingung. (Was ist, wenn die Datei dazwischen verschwindet?)
Alex L
34

Wie wäre es mit einer authentischen ternären Operation im Geiste von Andy Jones 'Antwort:

os.remove(fn) if os.path.exists(fn) else None
Tim Keating
quelle
41
Hässlicher Missbrauch von Ternären.
Bgusach
19
@BrianHVB Da Ternaries dazu dienen, basierend auf einer Bedingung zwischen zwei Werten zu wählen, keine Verzweigung durchzuführen.
Bgusach
1
Ich verwende keine Ausnahmen für die Flusskontrolle. Sie erschweren das Verständnis des Codes und können vor allem einen anderen Fehler maskieren (z. B. ein Berechtigungsproblem, das das Löschen einer Datei blockiert), das zu einem stillen Fehler führt.
Ed King
11
Das ist nicht atomar. Die Datei kann zwischen Aufrufen gelöscht und entfernt werden. Es ist sicherer, den Vorgang zu versuchen und ihn fehlschlagen zu lassen.
ConnorWGarvey
1
@ nam-g-vu Nur zu Ihrer Information, ich habe Ihre Bearbeitung zurückgesetzt, weil Sie im Grunde nur die Syntax des ursprünglichen Fragestellers als Alternative hinzugefügt haben. Da sie nach etwas anderem gesucht haben, glaube ich nicht, dass die Bearbeitung für diese spezielle Antwort von Bedeutung ist.
Tim Keating
9

Eine andere Möglichkeit, festzustellen, ob die Datei (oder Dateien) vorhanden ist, und sie zu entfernen, ist die Verwendung des Modulglob.

from glob import glob
import os

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

Glob findet alle Dateien, die das Muster mit einem * nix-Platzhalter auswählen könnten, und durchläuft die Liste.

Jotacor
quelle
9

Verwenden Sie ab Python 3.8 missing_ok=Trueund pathlib.Path.unlink( Dokumente hier )

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
quelle
1
Beste Antwort für praktisches Python3 meiner Meinung nach.
mrgnw
7

Matts Antwort ist die richtige für ältere Pythons und Kevins die richtige Antwort für neuere.

Wenn Sie die Funktion nicht kopieren möchten silentremove, wird diese Funktionalität in path.py als remove_p angezeigt :

from path import Path
Path(filename).remove_p()
Jason R. Coombs
quelle
6
if os.path.exists(filename): os.remove(filename)

ist ein Einzeiler.

Viele von Ihnen mögen anderer Meinung sein - möglicherweise aus Gründen wie der Betrachtung der vorgeschlagenen Verwendung von Ternären als "hässlich" -, aber dies wirft die Frage auf, ob wir Menschen zuhören sollten, die an hässliche Standards gewöhnt sind, wenn sie etwas Nicht-Standard "hässlich" nennen.

DevonMcC
quelle
3
Das ist sauber - ich verwende keine Ausnahmen für die Flusskontrolle. Sie erschweren das Verständnis des Codes und können vor allem einen anderen Fehler maskieren (z. B. ein Berechtigungsproblem, das das Löschen einer Datei blockiert), das zu einem stillen Fehler führt.
Ed King
2
Es ist nicht schön, weil davon ausgegangen wird, dass es nur einen Prozess gibt, der den Dateinamen ändert. Es ist nicht atomar. Es ist sicher und korrekt, den Vorgang zu versuchen und ordnungsgemäß fehlzuschlagen. Es ist ärgerlich, dass Python nicht standardisieren kann. Wenn wir ein Verzeichnis hätten, würden wir shutil verwenden und es würde genau das unterstützen, was wir wollen.
ConnorWGarvey
2

In Python 3.4 oder einer höheren Version wäre der pythonische Weg:

import os
from contextlib import suppress

with suppress(OSError):
    os.remove(filename)
Ross Castroverde
quelle
3
Dies unterscheidet sich nicht wesentlich von der hier angebotenen Antwort .
chb
1

Etwas wie das? Nutzt die Kurzschlussauswertung. Wenn die Datei nicht vorhanden ist, kann die gesamte Bedingung nicht wahr sein, sodass Python den zweiten Teil nicht auswertet.

os.path.exists("gogogo.php") and os.remove("gogogo.php")
Andy Jones
quelle
25
Dies ist definitiv nicht "pythonischer" - tatsächlich warnt Guido speziell davor und bezeichnet dies als "Missbrauch" der booleschen Operatoren.
Abarnert
1
Oh, ich stimme zu - ein Teil der Frage wurde nach einem einzeiligen Weg gestellt und dies war das erste, was mir in den Sinn kam
Andy Jones
4
Nun, Sie könnten es auch zu einem Einzeiler machen, indem Sie einfach die neue Zeile nach dem Doppelpunkt entfernen ... Oder, noch besser, Guide fügte widerwillig den if-Ausdruck hinzu, um die Leute davon abzuhalten, "die booleschen Operatoren zu missbrauchen", und es gibt eine großartige Gelegenheit, dies zu beweisen dass alles missbraucht werden kann: os.remove ("gogogo.php") wenn os.path.exists ("gogogo.php") sonst keine. :)
abarnert
0

Ein KISS-Angebot:

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

Und dann:

remove_if_exists("my.file")
Baz
quelle
1
Wenn Sie eine ganze Funktion schreiben müssen, verfehlt dies den Punkt der Einzeiler
Ion Lesan
@Ion Lesan Das OP ist der "beste" Weg, um dieses Problem zu lösen. Ein Einzeiler ist niemals ein besserer Weg, wenn er die Lesbarkeit gefährdet.
Baz
Angesichts der inhärent weit gefassten Definition von "am besten" werde ich nicht in diesem Sinne argumentieren, obwohl dies eindeutig von TOCTOU beeinflusst wird. Und definitiv keine KISS-Lösung.
Ion Lesan
@Matt Stimmt, aber leiden nicht einige der hier angebotenen Lösungen unter diesem Problem?
Baz
0

Dies ist eine andere Lösung:

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

Eine andere Lösung mit Ihrer eigenen Nachricht in Ausnahme.

import os

try:
    os.remove(filename)
except:
    print("Not able to delete the file %s" % filename)
Rishi Bansal
quelle
-1

Ich habe verwendet rm, um zu erzwingen, nicht vorhandene Dateien mit --preserve-rootals Option zu löschen rm.

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

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

Wir können auch safe-rm ( sudo apt-get install safe-rm) verwenden

Safe-rm ist ein Sicherheitstool, das das versehentliche Löschen wichtiger Dateien verhindern soll, indem / bin / rm durch einen Wrapper ersetzt wird, der die angegebenen Argumente mit einer konfigurierbaren schwarzen Liste von Dateien und Verzeichnissen vergleicht, die niemals entfernt werden sollten.

Zuerst überprüfe ich, ob Ordner- / Dateipfad vorhanden ist oder nicht. Dadurch wird verhindert, dass die Variable fileToRemove /folderToRemove to the string-r / `festgelegt wird.


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
quelle
1
Die Verwendung einer Shell für etwas, das so trivial ist, ist übertrieben und dieser Ansatz funktioniert auch nicht plattformübergreifend (z. B. Windows).
Nabla
4
Die Verwendung einer Shell anstelle der Standardbibliothek (z. B. os.remove) ist immer eine der am wenigsten pythonischen / sauberen Methoden, um etwas zu tun. Beispielsweise müssen Sie von der Shell zurückgegebene Fehler manuell behandeln.
Nabla
1
Ich habe meine Antwort hinzugefügt, um sie rmsicher zu verwenden und zu verhindern rm -r /. @ JonBrave
Alper
1
rm -f --preserve-rootist nicht gut genug ( --preserve-rootist wahrscheinlich sowieso die Standardeinstellung). Ich gab -r / als Beispiel , was ist, wenn es -r /homeoder was auch immer? Sie wollen wahrscheinlich rm -f -- $fileToRemove, aber das ist nicht der Punkt.
JonBrave
3
Nicht so, wie Sie es verwendet haben, mit einem Variablennamen (Umgebungsvariable) und ohne Anführungszeichen und ohne Schutz, nein. Und nicht für diese Frage, nein. Es os.system('rm ...')ist äußerst gefährlich, Unachtsame auszusetzen.
JonBrave