Il modo più pythonic per eliminare un file che potrebbe non esistere


453

Voglio eliminare il file filenamese esiste. È corretto dirlo

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

Esiste un modo migliore? Un modo a una linea?


7
Vuoi provare a eliminare un file se esiste (e fallisci se non hai le autorizzazioni) o fare una cancellazione del massimo sforzo e non ti viene mai restituito un errore in faccia?
Donal Fellows

Volevo fare "il primo" di ciò che diceva @DonalFellows. Per quello, immagino che il codice originale di Scott sarebbe un buon approccio?
LarsH,

Crea una funzione chiamata unlinke inseriscila nello spazio dei nomi PHP.
lama12345,

1
@LarsH Vedi il secondo blocco di codice della risposta accettata. Aumenta l'eccezione se l'eccezione è tutt'altro che un errore "nessun file o directory".
jpmc26,

Risposte:


613

Un modo più pitonico sarebbe:

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

Anche se questo prende ancora più righe e sembra molto brutto, evita la chiamata non necessaria os.path.exists()e segue la convenzione python di abusare delle eccezioni.

Potrebbe essere utile scrivere una funzione per fare questo per te:

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
Ma questo passerebbe se l'operazione di rimozione fallisse (leggi solo il file system o qualche altro problema imprevisto)?
Scott C Wilson,

136
Inoltre, il fatto che il file esista quando os.path.exists()viene eseguito non significa che esiste quando os.remove()viene eseguito.
kindall

8
Il mio +1, ma l'uso eccessivo delle eccezioni non è una convenzione Python :) O è vero?
pepr

8
@pepr Stavo solo criticando con umorismo il modo in cui le eccezioni fanno parte del normale comportamento in Python. Ad esempio, gli iteratori devono sollevare eccezioni per interrompere l'iterazione.
Matt,

5
+1 perché non riesco a +2. Oltre ad essere più Pythonic, questo è in realtà corretto, mentre l'originale non lo è, per il motivo suggerito. Condizioni di gara del genere portano a buche di sicurezza, bug difficili da
ripetere

160

Preferisco sopprimere un'eccezione piuttosto che verificare l'esistenza del file, per evitare un bug TOCTTOU . La risposta di Matt ne è un buon esempio, ma possiamo semplificarla leggermente in Python 3, usando contextlib.suppress():

import contextlib

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

Se filenameè un pathlib.Pathoggetto anziché una stringa, possiamo chiamarne il .unlink()metodo invece di usarlo os.remove(). Nella mia esperienza, gli oggetti Path sono più utili delle stringhe per la manipolazione del filesystem.

Poiché tutto in questa risposta è esclusivo di Python 3, fornisce ancora un altro motivo per l'aggiornamento.


8
Questo è il modo più pitonico di dicembre 2015. Python continua a evolversi.
Mayank Jaiswal,

2
Non ho trovato alcun metodo remove () per oggetti pathlib.Path su Python 3.6
BrianHVB

1
@jeffbyrnes: definirei una violazione dello Zen di Python: "Dovrebbe esserci uno - e preferibilmente solo un - modo invadente per farlo." Se avessi due metodi che facessero la stessa cosa, finiresti per mescolarli nell'esecuzione del codice sorgente, che sarebbe più difficile da seguire per il lettore. Ho il sospetto che volessero coerenza con unlink(2), che è di gran lunga la più antica interfaccia rilevante qui.
Kevin,

1
@nivk: se hai bisogno di una exceptclausola, allora dovresti usare try/ except. Non può essere abbreviato in modo significativo, perché è necessario disporre di una riga per introdurre il primo blocco, il blocco stesso, una riga per introdurre il secondo blocco e quindi quel blocco, quindi try/ exceptè già il più conciso possibile.
Kevin

1
Vale la pena sottolineare che, a differenza di un blocco try / tranne, questa soluzione significa che non è necessario pasticciare creando un'eccezione al fine di garantire che le metriche di copertura del test siano pertinenti.
thclark,

50

os.path.existsritorna Trueper cartelle e file. Prendi in considerazione l'utilizzo os.path.isfileper verificare l'esistenza del file.


4
Ogni volta che testiamo l'esistenza e poi rimuoviamo in base a quel test, ci stiamo aprendo a una condizione di razza. (E se il file scompare nel mezzo?)
Alex L

34

Nello spirito della risposta di Andy Jones, che ne dici di un'autentica operazione ternaria:

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

41
Brutto uso improprio di ternari.
bgusach,

19
@BrianHVB Perché i ternari sono lì per scegliere tra due valori basati su una condizione, non per fare ramificazioni.
bgusach

1
Non mi piace usare le eccezioni per il controllo del flusso. Rendono il codice difficile da comprendere e, cosa più importante, possono mascherare altri errori che si verificano (come un problema di autorizzazione che blocca l'eliminazione di un file) che causerà un errore silenzioso.
Ed King,

11
Questo non è atomico. Il file può essere eliminato tra le chiamate esistenti e rimuoverlo. È più sicuro tentare l'operazione e consentirne il fallimento.
ConnorWGarvey

1
@ nam-g-vu Solo FYI, ho ripristinato la tua modifica perché in pratica hai appena aggiunto la sintassi del questionario originale come alternativa. Dal momento che stavano cercando qualcosa di diverso, non credo che la modifica sia fondamentale per questa particolare risposta.
Tim Keating

10

A partire da Python 3.8, usa missing_ok=Truee pathlib.Path.unlink( documenti qui )

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 migliore risposta per il pratico python3 secondo me.
mrgnw,

9

Un altro modo per sapere se il file (o i file) esiste e per rimuoverlo è usare il modulo glob.

from glob import glob
import os

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

Glob trova tutti i file che potrebbero selezionare il modello con un carattere jolly * nix e scorre l'elenco.



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

è un liner.

Molti di voi potrebbero non essere d'accordo - probabilmente per ragioni come considerare brutto l'uso proposto di ternari - ma questo pone la domanda se dovremmo ascoltare le persone abituate a standard brutti quando chiamano qualcosa di "brutto" non standard.


3
questo è pulito - Non mi piace usare le eccezioni per il controllo del flusso. Rendono il codice difficile da comprendere e, cosa più importante, possono mascherare altri errori che si verificano (come un problema di autorizzazione che blocca l'eliminazione di un file) che causerà un errore silenzioso.
Ed King,

2
Non è carino perché presuppone che ci sia un solo processo che modificherà il nome del file. Non è atomico. È sicuro e corretto tentare l'operazione e fallire con grazia. È fastidioso che Python non possa standardizzare. Se avessimo una directory, useremmo shutil e supporterebbe esattamente ciò che vogliamo.
ConnorWGarvey,

2

In Python 3.4 o versione successiva, il modo pitone sarebbe:

import os
from contextlib import suppress

with suppress(OSError):
    os.remove(filename)

3
Ciò non differisce sostanzialmente dalla risposta offerta qui .
chb

1

Qualcosa come questo? Sfrutta la valutazione del corto circuito. Se il file non esiste, l'intero condizionale non può essere vero, quindi Python non disturberà la valutazione della seconda parte.

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

25
Questo non è sicuramente "più pitonico" - in effetti, è qualcosa di cui Guido specificamente mette in guardia e che definisce "abuso" degli operatori booleani.
abarnert,

1
oh, sono d'accordo - parte della domanda è stata posta in una riga e questa è stata la prima cosa che mi è venuta in mente
Andy Jones,

4
Bene, potresti anche trasformarlo in una riga solo rimuovendo la nuova riga dopo i due punti ... O, ancora meglio, la Guida ha aggiunto a malincuore l'espressione if per impedire alle persone di "abusare degli operatori booleani", e c'è una grande opportunità per dimostrare che qualcosa può essere abusato: os.remove ("gogogo.php") se os.path.exists ("gogogo.php") altrimenti Nessuno. :)
abarnert,

0

Un'offerta KISS:

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

E poi:

remove_if_exists("my.file")

1
Se devi scrivere un'intera funzione, in un certo senso manca il punto di una
battuta

@Ion Lesan L'OP sta cercando il modo "migliore" per risolvere questo problema. Un liner non è mai un modo migliore se mette a repentaglio la leggibilità.
Baz,

Data la definizione intrinsecamente ampia di "migliore", non ho intenzione di discutere in questo senso, sebbene sia chiaramente influenzato da TOCTOU. E sicuramente non è una soluzione KISS.
Ion Lesan,

@Matt True, ma un certo numero di soluzioni offerte qui non presenta questo problema?
Baz,

0

Questa è un'altra soluzione:

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

0

Un'altra soluzione con il tuo messaggio in eccezione.

import os

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

-1

Ho usato rmquale può forzare per eliminare i file inesistenti con --preserve-rootcome opzione a rm.

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

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

Possiamo anche usare safe-rm ( sudo apt-get install safe-rm)

Safe-rm è uno strumento di sicurezza inteso a prevenire la cancellazione accidentale di file importanti sostituendo / bin / rm con un wrapper, che verifica gli argomenti forniti rispetto a una lista nera configurabile di file e directory che non devono mai essere rimossi.

Innanzitutto controllo se esiste il percorso della cartella / del file. Ciò impedirà di impostare la variabile 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
Usare una shell per qualcosa di così banale è eccessivo e anche questo approccio non funziona su più piattaforme (ad es. Windows).
Nabla,

4
L'utilizzo di una shell anziché della libreria standard (ad esempio os.remove) è sempre uno dei modi meno pythonic / clean di fare qualcosa. Ad esempio, è necessario gestire manualmente gli errori restituiti dalla shell.
Nabla

1
Ho aggiunto la mia risposta per usare in rmsicurezza e prevenire rm -r /. @JonBrave
Alper

1
rm -f --preserve-rootnon è abbastanza buono ( --preserve-rootè probabilmente il valore predefinito comunque). Ho dato -r / come esempio , se fosse -r /homeo altro? Probabilmente vuoi rm -f -- $fileToRemove, ma non è questo il punto.
JonBrave,

3
Non nel modo in cui l'hai usato, con un nome di variabile (variabile di ambiente), senza virgolette e senza protezione, no. E non per questa domanda, no. Esporre gli incauti a os.system('rm ...')è estremamente pericoloso, mi dispiace.
JonBrave,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.