ChatGPT解决这个技术问题 Extra ChatGPT

Most pythonic way to delete a file which may not exist

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?

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?
I wanted to do "the former" of what @DonalFellows said. For that, I guess Scott's original code would be a good approach?
Make a function called unlink and put it in namespace PHP.
@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.

E
Enno Shioji

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

But would this pass if the remove operation failed (read only file system or some other unexpected issue)?
Also, the fact that the file exists when os.path.exists() is executed does not mean that it exists when os.remove() is executed.
My +1, but overusing of exceptions is not a Python convention :) Or is it?
@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.
+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.
K
Kevin

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.


This is the most pythonic way as on December 2015. Python keeps evolving though.
I found no remove() method for pathlib.Path objects on Python 3.6
@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.
@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.
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.
w
wkeithvan

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()

Best answer for practical python3 in my opinion.
I use this in 3.8, but as Path(filename).unlink(missing_ok=True)
M
Martin Thoma

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


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?)
T
Tim Keating

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

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

Ugly misuse of ternaries.
@BrianHVB Because ternaries are there to choose between two values based on a condition, not to do branching.
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.
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.
@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.
D
DevonMcC
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".


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.
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.
This is OK for your private tool scripts. This is NOT OK for servers. reason being: race condition vulnerability
j
jotacor

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.


N
Nam G VU

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()

R
Ross Castroverde

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

import os
from contextlib import suppress

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

This does not differ substantively from the answer offered here.
A
Andy Jones

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")

This is definitely not "more Pythonic"—in fact, it's something Guido specifically warns about, and refers to as "abuse" of the boolean operators.
oh, I agree - part of the question asked for a one line way and this was the first thing that popped into my head
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. :)
P
Paul

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 OSErrorFileNotFoundError
    pass

B
Baz

A KISS offering:

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

And then:

remove_if_exists("my.file")

If you have to write a whole function it kind of misses the point of one-liners
@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.
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.
@Matt True but don't a number of solutions offered here suffer from this issue?
K
Kian

This is another solution:

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

R
Rags

This will do in one line as well as checks if the path exists, else nothing really happens

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


whoever voted it down, mind explaining what's wrong with this approach? This works perfectly well for me.