635

I want to delete the file filename if it exists. Is it proper to say

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

Is there a better way? A one-line way?

Scott C Wilson
  • 19,102
  • 10
  • 61
  • 83
  • 8
    Do you want to try to delete a file if it exists (and fail if you lack permissions) or to do a best-effort delete and never have an error thrown back in your face? – Donal Fellows May 31 '12 at 20:09
  • 1
    I wanted to do "the former" of what @DonalFellows said. For that, I guess Scott's original code would be a good approach? – LarsH Dec 11 '13 at 15:48
  • 1
    Make a function called `unlink` and put it in namespace PHP. – kungfooman Jan 31 '14 at 18:50
  • 2
    @LarsH See the second code block of the accepted answer. It reraises the exception if the exception is anything but a "no such file or directory" error. – jpmc26 Feb 11 '14 at 20:16

14 Answers14

798

A more pythonic way would be:

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

Although this takes even more lines and looks very ugly, it avoids the unnecessary call to os.path.exists() and follows the python convention of overusing exceptions.

It may be worthwhile to write a function to do this for you:

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
Enno Shioji
  • 26,542
  • 13
  • 70
  • 109
Matt
  • 21,026
  • 18
  • 63
  • 115
  • 26
    But would this pass if the remove operation failed (read only file system or some other unexpected issue)? – Scott C Wilson May 31 '12 at 20:12
  • 173
    Also, the fact that the file exists when `os.path.exists()` is executed does not mean that it exists when `os.remove()` is executed. – kindall May 31 '12 at 20:12
  • 2
    @ScottWilson you can use the errno module to figure that out. (I will update my answer). – Matt May 31 '12 at 20:18
  • 14
    My +1, but overusing of exceptions is not a Python convention :) Or is it? – pepr May 31 '12 at 20:49
  • 10
    @pepr I was just humorously criticizing how exceptions are part of normal behavior in python. For example, iterators **must** raise exceptions in order to stop iterating. – Matt May 31 '12 at 21:37
  • 6
    +1 because I can't +2. Besides being more Pythonic, this one is actually correct, while the original is not, for the reason kindall suggested. Race conditions like that lead to security holes, hard-to-repro bugs, etc. – abarnert May 31 '12 at 21:39
  • 3
    @Matt, exceptions are not errors but just exceptional incidents; so raising one when an iterator cannot continue is just following this logic. – Alfe Sep 04 '12 at 15:24
  • 1
    Just to say that precisely this is discussed here: https://www.python.org/dev/peps/pep-3151/#lack-of-fine-grained-exceptions – Mr_and_Mrs_D Nov 11 '15 at 13:17
  • Is exception more overhead than if? – liang Feb 26 '16 at 17:32
  • @liang: Performance-wise, yes. But maybe not noticeably for most Python purposes. – Matt Mar 11 '16 at 22:29
  • 1
    As of Python 3.3, you often might want to catch `FileNotFoundError` instead of `OSError`. – stephan Jun 27 '16 at 09:42
  • @stephan: According to the [Python 3 docs](https://docs.python.org/3/library/os.html#os.remove), `os.remove` still raises `OSError` and not `FileNotFoundError`. – Matt Jul 01 '16 at 18:13
  • 5
    @Matt: sure, `os.remove` raises `OSError`. [Since Python 3.3](https://docs.python.org/3/whatsnew/3.3.html#pep-3151-reworking-the-os-and-io-exception-hierarchy) this is a base class for various OS errors. But you no longer have to check `errno` to get more fine-grained information. Instead, it is possible to only catch `FileNotFoundError`, but not `PermissionError` or `IsADirectoryError`. As @Mr_and_Mrs_D already mentioned, [PEP 3151](https://www.python.org/dev/peps/pep-3151/#lack-of-fine-grained-exceptions) uses exactly your answer as a motivating example. Or am I missing something? – stephan Jul 01 '16 at 21:26
  • 2
    @stephan: Oh, I see. `FileNotFoundError` is derived from `OSError` and it is actually `FileNotFoundError` that is raised. It seems the docs for the `os` library haven't been updated. Good catch! PS: I find it a little eerie that the PEP uses my examples letter-for-letter, even though it seems to have been written before I posted this answer... – Matt Jul 12 '16 at 17:46
  • 1
    Note that, if `filename` happens to be `None`, `os.remove()` raises a `TypeError` – djvg Oct 18 '17 at 07:07
  • I understand that this is probably the best way to remove a file, but I really hate using `try/except` clauses in my code except when absolutely necessary. It'd be nice if this were built into the `os` module. – Michael Hays Feb 12 '18 at 21:13
  • 1
    @MichaelHays: That was my point when I said it "follows the python convention of overusing exceptions". It's the Python style of doing things, and it's built into the language relatively efficiently. – Matt Feb 16 '18 at 18:36
  • 1
    @Matt I'm not saying that this isn't an acceptable answer, just that I'm not going to use it out of personal preference. – Michael Hays Feb 19 '18 at 17:06
  • This does confuse some potentially useful cases, such as an OSError being raised when the file is in use by another application (which just happened to me!) – jellyberg Jul 08 '19 at 00:45
  • 1
    @jellyberg: The second version that checks the error code should work, no? – Matt Jul 19 '19 at 08:26
  • 2
    isn't OSError a bit general? Wouldn't FileNotFoundError do it? – Frode Akselsen Dec 17 '19 at 20:40
  • How about using `shutil.rmtree(path)` if it is a folder and use `os.remove(filename)` if it is a file – alper Jul 27 '20 at 11:05
  • I like the function `silentremove` but I'm renaming it to `remove_existing` – WinEunuuchs2Unix Aug 12 '21 at 02:52
  • Even if the file exists, the caller may not have the permissions to delete it. Race conditions aside, checking for existence is no guarantee of successful deletion. – Seva Alekseyev Dec 09 '22 at 16:41
  • 1
    Unfortunately people way overuse exceptions, which as you noted yourself is ugly not readable and essentially does not what asked. The task was "delete file which might not exist"while the implementation does "delete no matter what and ignore the error. Now if the file does exist but could not be deleted for any other reason the operation will fail silently. See you just introduced a hard to find bug out of nothing. – user2555515 May 03 '23 at 18:13
261

I prefer to suppress an exception rather than checking for the file's existence, to avoid a TOCTTOU bug. Matt's answer is a good example of this, but we can simplify it slightly under Python 3, using contextlib.suppress():

import contextlib

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

If filename is a pathlib.Path object instead of a string, we can call its .unlink() method instead of using os.remove(). In my experience, Path objects are more useful than strings for filesystem manipulation.

Since everything in this answer is exclusive to Python 3, it provides yet another reason to upgrade.

Kevin
  • 28,963
  • 9
  • 62
  • 81
  • 14
    This is the most pythonic way as on December 2015. Python keeps evolving though. – Mayank Jaiswal Dec 16 '15 at 11:37
  • 2
    I found no remove() method for pathlib.Path objects on Python 3.6 – Brian H. Mar 01 '16 at 16:31
  • @BrianHVB: Thanks, fixed. – Kevin Mar 01 '16 at 16:43
  • Not in Python 2.* – Evgen Mar 23 '17 at 23:20
  • @EvgeniiPuchkaryov: Yes, I said as much in the last paragraph. – Kevin Mar 24 '17 at 00:29
  • Perhaps to more clearly state, per the [Python 3 docs](https://docs.python.org/3.6/library/pathlib.html#pathlib.Path.unlink), `Path.unlink()` will delete ordinary files, or remove a symlink. – jeffbyrnes Jan 31 '18 at 18:19
  • @jeffbyrnes: I'm pretty sure it's identical to `os.remove()` in that regard (as well as [`rm(1)`](https://linux.die.net/man/1/rm) and [`unlink(2)`](https://linux.die.net/man/2/unlink)). Since this seems to be a pretty universal behavior, I'm not sure what you want me to add to this answer in particular that wouldn't also need to go on every other answer here. – Kevin Jan 31 '18 at 18:55
  • @Kevin it was unclear to me until I went & re-read the docs, _after_ reading your answer here, that `Path.unlink()` also deletes regular files. Seems like maybe they should alias `Path.rm()` or `Path.remove()` to `Path.unlink()`, even if it’s just a convenience/clarity thing. – jeffbyrnes Jan 31 '18 at 19:14
  • 1
    @jeffbyrnes: I'd call that a violation of the Zen of Python: "There should be one-- and preferably only one --obvious way to do it." If you had two methods which did the same thing, you would end up with a mixture of them in running source code, which would be harder for the reader to follow. I suspect they wanted consistency with `unlink(2)`, which is by far the oldest relevant interface here. – Kevin Feb 20 '18 at 02:54
  • Yet another reason to upgrade to Python 3 - very true, even 4 years later. – hans Mar 29 '18 at 09:32
  • I replaced the second line to os.remove(Path(chunk_name)) and got the code for removing a file if previously present – alemol Dec 08 '18 at 20:09
  • Can this solution be modified to do `something()` with the failing filename if the exception is raised? That is, can this replicate the `except` clause's functionality? – nivk Jan 29 '19 at 21:37
  • 1
    @nivk: If you need an `except` clause, then you should use `try`/`except`. It cannot be meaningfully shortened, because you must have a line to introduce the first block, the block itself, a line to introduce the second block, and then that block, so `try`/`except` is already as terse as possible. – Kevin Jan 29 '19 at 21:41
  • 1
    It's worth pointing out that unlike a try/except block, this solution means you don't have to mess around creating an exception in order to ensure that test coverage metrics are relevant. – thclark Oct 28 '19 at 10:02
116

As of Python 3.8, use missing_ok=True and pathlib.Path.unlink (docs here)

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
  • 1,755
  • 1
  • 12
  • 17
56

os.path.exists returns True for folders as well as files. Consider using os.path.isfile to check for whether the file exists instead.

Martin Thoma
  • 124,992
  • 159
  • 614
  • 958
abought
  • 2,652
  • 1
  • 18
  • 13
  • 12
    Any time we test for existence and then remove based on that test, we are opening ourselves up to a race condition. (What if the file disappears in between?) – Alex L Sep 04 '19 at 13:27
47

In the spirit of Andy Jones' answer, how about an authentic ternary operation:

os.remove(fn) if os.path.exists(fn) else None
Tim Keating
  • 6,443
  • 4
  • 47
  • 53
  • 24
    @BrianHVB Because ternaries are there to choose between two values based on a condition, not to do branching. – bgusach Mar 01 '16 at 17:29
  • 2
    I don't like to use exceptions for flow control. They make code difficult to understand and more importantly can mask some other error occurring (like a permission issue blocking a file delete) which will cause a silent fail. – Ed King Oct 24 '17 at 17:06
  • 18
    This is not atomic. The file can be deleted between calls to exists and remove. It's safer to attempt the operation and allow it to fail. – ConnorWGarvey Dec 28 '17 at 17:09
  • @EdKing That is true if the "exception" is actually a possibility. According to _PP_ the book, exceptions should be used for what really don't mean to happen, e.g. `/etc/passwd` don't exist. – InQβ Mar 21 '18 at 12:48
  • 1
    @nam-g-vu Just FYI, I rolled back your edit because you basically just added the original questioner's syntax as an alternative. Since they were looking for something different than that, I don't feel that edit is germane to this particular answer. – Tim Keating Feb 14 '19 at 15:53
  • Any time we test for existence and then remove based on that test, we are opening ourselves up to a race condition. (What if the file disappears in between?) – Alex L Sep 04 '19 at 13:28
  • It's easier to write `if os.path.exists(fn): os.remove(fn)` – acmpo6ou Sep 02 '21 at 06:15
20
if os.path.exists(filename): os.remove(filename)

is a one-liner.

Many of you may disagree - possibly for reasons like considering the proposed use of ternaries "ugly" - but this begs the question of whether we should listen to people used to ugly standards when they call something non-standard "ugly".

DevonMcC
  • 435
  • 4
  • 4
  • 7
    this is clean -- I don't like to use exceptions for flow control. They make code difficult to understand and more importantly can mask some other error occurring (like a permission issue blocking a file delete) which will cause a silent fail. – Ed King Oct 24 '17 at 17:05
  • 3
    It's not pretty because it assumes there is only one process that will modify filename. It's not atomic. It's safe and correct to attempt the operation and fail gracefully. It's annoying that Python can't standardize. If we had a directory, we'd use shutil and it would support exactly what we want. – ConnorWGarvey Dec 28 '17 at 17:14
  • 1
    This is OK for your private tool scripts. This is NOT OK for servers. reason being: race condition vulnerability – Walter May 27 '21 at 22:13
9

Matt's answer is the right one for older Pythons and Kevin's the right answer for newer ones.

If you wish not to copy the function for silentremove, this functionality is exposed in path.py as remove_p:

from path import Path
Path(filename).remove_p()
Nam G VU
  • 33,193
  • 69
  • 233
  • 372
Jason R. Coombs
  • 41,115
  • 10
  • 83
  • 93
8

Another way to know if the file (or files) exists, and to remove it, is using the module glob.

from glob import glob
import os

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

Glob finds all the files that could select the pattern with a *nix wildcard, and loops the list.

jotacor
  • 2,878
  • 1
  • 18
  • 19
5

Since Python 3.3 you can use FileNotFoundError which is more correct than the accepted version since it doesn't ignore other possible errors.

try:
    os.remove(filename)
except FileNotFoundError:
    pass
Paul
  • 6,061
  • 6
  • 39
  • 70
3

In Python 3.4 or later version, the pythonic way would be:

import os
from contextlib import suppress

with suppress(OSError):
    os.remove(filename)
  • 4
    This does not differ substantively from [the answer offered here](https://stackoverflow.com/a/27045091/148680). – chb Apr 18 '18 at 01:54
1

Something like this? Takes advantage of short-circuit evaluation. If the file does not exist, the whole conditional cannot be true, so python will not bother evaluation the second part.

os.path.exists("gogogo.php") and os.remove("gogogo.php")
Andy Jones
  • 6,205
  • 4
  • 31
  • 47
  • 31
    This is definitely not "more Pythonic"—in fact, it's something Guido specifically warns about, and refers to as "abuse" of the boolean operators. – abarnert May 31 '12 at 21:38
  • 1
    oh, I agree - part of the question asked for a one line way and this was the first thing that popped into my head – Andy Jones May 31 '12 at 22:21
  • 5
    Well, you could also make it a one-liner by just removing the newline after the colon… Or, even better, Guide grudgingly added the if-expression to stop people from "abusing the boolean operators", and there's a great opportunity to prove that anything can be abused: os.remove("gogogo.php") if os.path.exists("gogogo.php") else None. :) – abarnert Jun 01 '12 at 19:03
1

A KISS offering:

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

And then:

remove_if_exists("my.file")
Baz
  • 12,713
  • 38
  • 145
  • 268
  • 2
    If you have to write a whole function it kind of misses the point of one-liners – Ion Lesan Aug 15 '18 at 02:12
  • 1
    @Ion Lesan The OP is after the "best" way to solve this problem. A one liner is never a better way if it jeopardizes readability. – Baz Aug 15 '18 at 14:49
  • Given the inherently broad definition of "best", I'm not going to argue in this sense, although it's clearly affected by TOCTOU. And definitely not a KISS solution. – Ion Lesan Aug 17 '18 at 23:21
  • @Matt True but don't a number of solutions offered here suffer from this issue? – Baz Dec 13 '18 at 16:50
0

This is another solution:

if os.path.isfile(os.path.join(path, filename)):
    os.remove(os.path.join(path, filename))
Kian
  • 1,319
  • 1
  • 13
  • 23
0

If glob is available.

list(map(os.remove, glob.glob("file/matching/pattern/*.csv")))

map to map the output of glob to os.remove.

list to invoke the map object. (Python3)

BATMAN
  • 375
  • 2
  • 14