Najbardziej pythonowy sposób usuwania pliku, który może nie istnieć


453

Chcę usunąć plik, filenamejeśli istnieje. Czy to właściwe powiedzieć?

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

Czy jest lepszy sposób? Jednowierszowy sposób?


7
Czy chcesz spróbować usunąć plik, jeśli istnieje (i nie powiedzie się, jeśli nie masz uprawnień), czy chcesz usunąć wszystko, co możliwe, i nigdy nie pojawi się błąd?
Donal Fellows

Chciałem zrobić „pierwsze” z tego, co powiedział @DonalFellows. W związku z tym sądzę, że oryginalny kod Scotta byłby dobrym podejściem?
LarsH,

Utwórz funkcję o nazwie unlinki umieść ją w przestrzeni nazw PHP.
lama12345

1
@LarsH Zobacz drugi blok kodu przyjętej odpowiedzi. Zgłasza wyjątek, jeśli wyjątek jest tylko błędem „brak takiego pliku lub katalogu”.
jpmc26

Odpowiedzi:


613

Bardziej pythonowym sposobem byłoby:

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

Chociaż wymaga to jeszcze większej liczby wierszy i wygląda bardzo brzydko, unika niepotrzebnego wywołania os.path.exists()i przestrzega konwencji pythonowej nadużywania wyjątków.

Warto napisać funkcję, która zrobi to za Ciebie:

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
Ale czy to przejdzie, jeśli operacja usunięcia nie powiedzie się (system plików tylko do odczytu lub inny nieoczekiwany problem)?
Scott C Wilson

134
Ponadto fakt, że plik istnieje, gdy os.path.exists()jest wykonywany, nie oznacza, że ​​istnieje podczas os.remove()wykonywania.
uprzejmie

8
Moje +1, ale nadużywanie wyjątków nie jest konwencją Pythona :) A może tak?
pepr

8
@pepr Właśnie humorystycznie krytykowałem, w jaki sposób wyjątki są częścią normalnego zachowania w Pythonie. Na przykład iteratory muszą zgłaszać wyjątki, aby zatrzymać iterację.
Matt

5
+1, ponieważ nie mogę +2. Oprócz tego, że jest bardziej Pythoniczny, ten jest w rzeczywistości poprawny, podczas gdy oryginał nie jest, z tego powodu, który sugeruje. Takie warunki wyścigowe prowadzą do luk w zabezpieczeniach, trudnych do naprawienia błędów itp.
abarnert

159

Wolę pomijać wyjątek niż sprawdzać istnienie pliku, aby uniknąć błędu TOCTTOU . Odpowiedź Matta jest tego dobrym przykładem, ale możemy to nieco uprościć w Pythonie 3, używając contextlib.suppress():

import contextlib

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

Jeśli filenamejest pathlib.Pathobiektem zamiast ciągu, możemy wywołać jego .unlink()metodę zamiast używać os.remove(). Z mojego doświadczenia wynika, że ​​obiekty Path są bardziej przydatne niż ciągi do manipulacji systemem plików.

Ponieważ wszystko w tej odpowiedzi dotyczy wyłącznie Pythona 3, jest to kolejny powód do aktualizacji.


7
Jest to najbardziej pythoniczny sposób jak w grudniu 2015. Python jednak ewoluuje.
Mayank Jaiswal

2
Nie znalazłem metody remove () dla obiektów pathlib.Path w Pythonie 3.6
BrianHVB

1
@jeffbyrnes: Nazwałbym to pogwałceniem Zen Pythona: „Powinien być jeden - a najlepiej tylko jeden - oczywisty sposób, aby to zrobić”. Gdybyś miał dwie metody, które zrobiły to samo, skończyłbyś się ich mieszaniem z uruchomionym kodem źródłowym, co byłoby trudniejsze dla czytelnika. Podejrzewam, że chcieli spójności unlink(2), która jest zdecydowanie najstarszym odpowiednim interfejsem tutaj.
Kevin,

1
@nivk: Jeśli potrzebujesz exceptklauzuli, powinieneś użyć try/ except. Nie można go znacząco skrócić, ponieważ musisz mieć linię do wprowadzenia pierwszego bloku, samego bloku, linię do wprowadzenia drugiego bloku, a następnie ten blok, więc try/ exceptjest już tak zwięzły, jak to możliwe.
Kevin

1
Warto zauważyć, że w przeciwieństwie do bloku try / try, to rozwiązanie oznacza, że ​​nie musisz się bawić, tworząc wyjątek, aby upewnić się, że wskaźniki zasięgu testu są odpowiednie.
thclark

50

os.path.existszwraca Truezarówno foldery, jak i pliki. Rozważ użycie, os.path.isfileaby sprawdzić, czy plik istnieje zamiast tego.


4
Za każdym razem, gdy testujemy istnienie, a następnie usuwamy na podstawie tego testu, otwieramy się na warunki wyścigu. (Co jeśli plik zniknie pomiędzy?)
Alex L

34

W duchu odpowiedzi Andy'ego Jonesa, co powiesz na autentyczną operację potrójną:

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

41
Brzydkie niewłaściwe użycie trójskładników.
bgusach

19
@BrianHVB Ponieważ trójskładniki mają do wyboru dwie wartości w zależności od warunku, a nie rozgałęzienia.
bgusach

1
Nie lubię używać wyjątków do kontroli przepływu. Utrudniają zrozumienie kodu i, co ważniejsze, mogą maskować niektóre inne występujące błędy (takie jak problem z uprawnieniami blokujący usunięcie pliku), który spowoduje cichą awarię.
Ed King

11
To nie jest atomowe. Plik można usuwać między wezwaniami do istnienia i usuwać. Bezpieczniej jest spróbować wykonać operację i pozwolić jej zakończyć się niepowodzeniem.
ConnorWGarvey

1
@ nam-g-vu Właśnie do twojej wiadomości, cofnąłem twoją edycję, ponieważ po prostu dodałeś oryginalną składnię pytającego jako alternatywę. Ponieważ szukali czegoś innego, nie wydaje mi się, aby edycja była związana z tą konkretną odpowiedzią.
Tim Keating

9

Innym sposobem na sprawdzenie, czy plik (lub pliki) istnieje, i usunięcie go, jest użycie modułu glob.

from glob import glob
import os

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

Glob znajduje wszystkie pliki, które mogłyby wybrać wzorzec z symbolem wieloznacznym * nix, i zapętla listę.


8

Począwszy od Python 3.8, użyj missing_ok=Truei pathlib.Path.unlink( dokumenty tutaj )

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
Najlepsza odpowiedź na praktyczne python3 moim zdaniem.
mrgnw


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

jest jednowarstwowy.

Wielu z was może się nie zgodzić - być może z powodów takich jak rozważenie proponowanego użycia trójek jako „brzydkich” - ale rodzi się pytanie, czy powinniśmy słuchać ludzi przyzwyczajonych do brzydkich standardów, gdy nazywają coś niestandardowego „brzydkim”.


3
to jest czyste - nie lubię używać wyjątków do kontroli przepływu. Utrudniają zrozumienie kodu i, co ważniejsze, mogą maskować niektóre inne występujące błędy (takie jak problem z uprawnieniami blokujący usunięcie pliku), który spowoduje cichą awarię.
Ed King

2
To nie jest ładne, ponieważ zakłada, że ​​istnieje tylko jeden proces, który zmodyfikuje nazwę pliku. To nie jest atomowe. Próba operacji jest bezpieczna i poprawna. To denerwujące, że Python nie może się ustandaryzować. Gdybyśmy mieli katalog, użylibyśmy shutil, który obsługiwałby dokładnie to, czego chcemy.
ConnorWGarvey,


1

Coś takiego? Wykorzystuje ocenę zwarcia. Jeśli plik nie istnieje, cały warunek nie może być prawdziwy, więc python nie będzie zawracał sobie głowy oceną drugiej części.

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

25
To zdecydowanie nie jest „bardziej pytoniczne” - w rzeczywistości jest to coś, o czym Guido wyraźnie ostrzega i które określa jako „nadużycie” operatorów logicznych.
abarnert

1
och, zgadzam się - część pytania zadawanego w jednej linii i to była pierwsza rzecz, która wpadła mi do głowy
Andy Jones

4
Cóż, możesz też uczynić z niego jedno-linijkę, usuwając po prostu nową linię po dwukropku… Lub, co lepiej, Guide niechętnie dodał wyrażenie „if”, aby powstrzymać ludzi przed „nadużywaniem operatorów boolowskich” i istnieje świetna okazja, aby udowodnić że wszystko może być nadużywane: os.remove („gogogo.php”), jeśli os.path.exists („gogogo.php”) else None. :)
abarnert

0

Oferta KISS:

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

I wtedy:

remove_if_exists("my.file")

1
Jeśli musisz napisać całą funkcję, to nie trafia w jedno-
linijki

@Ion Lesan OP poszukuje „najlepszego” sposobu rozwiązania tego problemu. Jeden wkładka nigdy nie jest lepszym sposobem, jeśli zagraża czytelności.
Baz

Biorąc pod uwagę z natury szeroką definicję „najlepszego”, nie zamierzam dyskutować w tym sensie, chociaż TOCTOU wyraźnie na to wpływa. I zdecydowanie nie jest to rozwiązanie KISS.
Ion Lesan,

@Matt Prawda, ale czy wiele rozwiązań tutaj nie cierpi z powodu tego problemu?
Baz

0

To jest inne rozwiązanie:

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

0

Inne rozwiązanie z twoim własnym komunikatem w wyjątku.

import os

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

-1

Użyłem rmktóre mogą wymusić usunięcie nieistniejących plików z --preserve-rootjako opcja rm.

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

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

Możemy również użyć safe-rm ( sudo apt-get install safe-rm)

Safe-rm to narzędzie bezpieczeństwa, którego zadaniem jest zapobieganie przypadkowemu usunięciu ważnych plików poprzez zamianę / bin / rm na opakowanie, które sprawdza podane argumenty względem konfigurowalnej czarnej listy plików i katalogów, których nigdy nie należy usuwać.

Najpierw sprawdzam, czy ścieżka do folderu / pliku istnieje, czy nie. Zapobiegnie to ustawianiu zmiennej 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
Używanie powłoki do czegoś tak trywialnego to przesada, a to podejście również nie będzie działać na różnych platformach (np. Windows).
Nabla

4
Użycie powłoki zamiast standardowej biblioteki (na przykład os.remove) jest zawsze jednym z najmniej pythonicznych / czystych sposobów zrobienia czegoś. Na przykład musisz ręcznie obsługiwać błędy zwracane przez powłokę.
Nabla

1
Dodałem swoją odpowiedź, aby korzystać rmbezpiecznie i zapobiegać rm -r /. @JonBrave
alper

1
rm -f --preserve-rootnie jest wystarczająco dobry (i --preserve-roottak prawdopodobnie jest domyślny). Podałem -r / jako przykład , co jeśli to -r /homeczy coś? Prawdopodobnie chcesz rm -f -- $fileToRemove, ale nie o to chodzi.
JonBrave

3
Nie w sposób, w jaki go używałeś, z nazwą zmiennej (zmienną środowiskową), bez cytowania i bez ochrony, nie. I nie w przypadku tego pytania, nie. Odsłonięcie nieostrożnego os.system('rm ...')jest niezwykle niebezpieczne, przepraszam.
JonBrave
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.