Most pythonic way to delete a file which may not exist
我想删除文件filename,如果它存在的话。说得对吗
1 2
| if os.path.exists(filename):
os.remove(filename) |
有更好的方法吗?单线方式?
- 您想尝试删除一个存在的文件(如果您没有权限,则会失败)还是尽最大努力删除一个文件,并且永远不会在您的脸上引发错误?
- 后者。尽最大努力删除。
- @马特把它钉住了。寻求宽恕,而不是许可。
- 我想做@donalfellows所说的"前者"。为此,我想斯科特的原始代码将是一个很好的方法?
- 生成一个名为unlink的函数,并将其放入名称空间php中。
- @拉尔斯看到了被接受答案的第二个代码块。如果异常不是"无此类文件或目录"错误,则会重新引发异常。
一个更像Python的方法是:
1 2 3 4
| try:
os.remove(filename)
except OSError:
pass |
尽管这需要更多的行,看起来非常难看,但它避免了不必要的调用os.path.exists(),并遵循了过度使用异常的Python约定。
为您编写这样的函数可能是值得的:
1 2 3 4 5 6 7 8
| 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 |
- 但是,如果删除操作失败(只读文件系统或其他一些意外问题),这会通过吗?
- 此外,执行os.path.exists()时文件存在的事实并不意味着执行os.remove()时文件存在。
- @Scottilson您可以使用errno模块来解决这个问题。(我会更新我的答案)。
- 我的+1,但是过度使用异常不是Python约定:)还是?
- @PeprI只是幽默地批评了异常是Python正常行为的一部分。例如,迭代器必须引发异常才能停止迭代。
- +因为我不能+2。除了更像是Python,这一个事实上是正确的,而原来是不正确的,出于某种原因,有人建议。这样的竞争条件会导致安全漏洞、难以修复的错误等。
- @Matt,异常不是错误,而是异常事件;所以当迭代器无法继续时引发异常只是遵循这个逻辑。
- 准确地说,这里讨论的是:python.org/dev/pep s/pep-3151/缺少细粒度异常‌&8203;s
- 异常是否比if更多的开销?
- @梁:表演方面,是的。但对于大多数Python来说,这可能并不明显。
- 从python 3.3开始,您可能经常希望捕获FileNotFoundError,而不是OSError。
- @Stephan:根据python 3文档,os.remove仍然提升OSError而不是FileNotFoundError。
- @马特:当然,埃多克斯1〔4〕提高了埃多克斯1〔3〕。由于python 3.3,这是各种操作系统错误的基类。但您不再需要检查errno以获得更细粒度的信息。相反,只能捕获FileNotFoundError,而不能捕获PermissionError或IsADirectoryError。正如@mr_和u mrs_d所提到的,PEP 3151将您的答案作为激励性的例子。还是我错过了什么?
- @斯蒂芬:哦,我明白了。FileNotFoundError源于OSError,实际上是FileNotFoundError被提升。似乎os库的文档没有更新。抓得好!附言:我觉得政治公众人物用我的例行公事来写信有点奇怪,尽管它似乎是在我发表这个答案之前写的…
- 注意,如果filename恰好是None的话,os.remove()就会增加TypeError。
- 我知道这可能是删除文件的最佳方法,但我真的不喜欢在代码中使用try/except子句,除非绝对必要。如果它内置在os模块中,那就太好了。
- @Michaelhays:这就是我说的"遵循使用过度异常的Python约定"时的观点。这是Python的做事风格,并且它相对高效地嵌入到语言中。
- @马特,我不是说这不是一个可以接受的答案,只是说我不会出于个人喜好使用它。
我更喜欢抑制异常,而不是检查文件是否存在,以避免toctou错误。Matt的答案就是一个很好的例子,但是我们可以在python 3下使用contextlib.suppress()稍微简化一下:
1 2 3 4
| import contextlib
with contextlib.suppress(FileNotFoundError):
os.remove(filename) |
如果filename是pathlib.Path对象而不是字符串,我们可以调用它的.unlink()方法而不是使用os.remove()方法。根据我的经验,对于文件系统操作,路径对象比字符串更有用。
由于这个答案中的所有内容都是Python3独有的,所以它提供了另一个升级的理由。
- 这是截至2015年12月的最Python的方式。但Python一直在进化。
- 我在python 3.6上找不到pathlib.path对象的remove()方法
- @布莱恩:谢谢,修好了。
- 不在python 2中。*
- @是的,我在最后一段也说过。
- 也许更清楚地说,根据python 3文档,Path.unlink()将删除普通文件,或删除符号链接。
- @杰夫伯恩斯:我敢肯定,在这方面,它和os.remove()是一样的(以及rm(1)和unlink(2)也是一样的)。因为这似乎是一个相当普遍的行为,我不确定你要我在这个答案中添加什么,特别是不需要在每个其他答案中都添加。
- @Kevin我不清楚,直到我重新阅读文档,在阅读了您的答案之后,Path.unlink()也删除了常规文件。似乎他们应该把Path.rm()或Path.remove()化名为Path.unlink(),即使这只是一个方便/清晰的东西。
- @Jeffbyrnes:我认为这违反了python的禅:"应该有一种——最好只有一种——显而易见的方法。"如果你有两种方法做同样的事情,那么你最终会在运行源代码时混合使用它们,这对读者来说是很难理解的。我怀疑他们想要与unlink(2)保持一致,这是迄今为止最古老的相关接口。
- 另一个升级到python 3的原因是非常正确的,甚至4年后。
- 我将第二行替换为os.remove(path(chunk_name)),并获得了删除文件的代码(如果以前存在的话)。
- 如果引发异常,是否可以将此解决方案修改为使用失败文件名执行something()?也就是说,这能复制except子句的功能吗?
- @nivk:如果你需要一个except条款,那么你应该使用try/except。它不能有意义地缩短,因为您必须有一行来引入第一个块,块本身,一行来引入第二个块,然后是该块,所以try/except已经尽可能简洁了。
对于文件夹和文件,os.path.exists返回True。考虑使用os.path.isfile检查文件是否存在。
根据安迪·琼斯的回答,一个真正的三元操作如何:
1
| os.remove(fn) if os.path.exists(fn) else None |
- 三元混用。
- @为什么丑?
- @Brianhvb是因为三元可以根据一个条件在两个值之间进行选择,而不是进行分支。
- 我不喜欢将异常用于流控制。它们使代码难以理解,更重要的是,它们可以掩盖发生的其他一些错误(如阻止文件删除的权限问题),这将导致静默失败。
- 这不是原子的。可以在调用exists和remove之间删除该文件。尝试操作并允许其失败更安全。
- @如果"例外"实际上是一种可能性,那么这是真的。根据pp这本书,例外情况应该用于真正不意味着发生的事情,例如/etc/passwd不存在。
- @nam-g-vu,仅供参考,我回滚了您的编辑,因为您基本上只是添加了原始提问者的语法作为替代。因为他们在寻找与此不同的东西,我不认为编辑与这个特定的答案密切相关。
另一种知道文件(或文件)是否存在并将其删除的方法是使用模块glob。
1 2 3 4 5
| from glob import glob
import os
for filename in glob("*.csv"):
os.remove(filename) |
glob查找所有可以用*nix通配符选择模式的文件,并循环列表。
马特的回答是对老年人的正确答案,凯文的回答是对较新人的正确答案。
如果不想复制silentremove的函数,则会在path.py中将此功能公开为remove_p:
1 2
| from path import Path
Path(filename).remove_p() |
1
| if os.path.exists(filename): os.remove(filename) |
是一个班轮。
你们中的许多人可能不同意——可能是因为考虑使用三元"丑"之类的原因——但这就引出了一个问题,即当人们把一些非标准的东西称为"丑"时,我们是否应该听取那些习惯于丑标准的人的意见。
- 这很干净——我不喜欢在流控制中使用异常。它们使代码难以理解,更重要的是,它们可以掩盖发生的其他一些错误(如阻止文件删除的权限问题),这将导致静默失败。
- 它并不漂亮,因为它假定只有一个进程可以修改文件名。它不是原子的。尝试操作并优雅地失败是安全和正确的。Python不能标准化真让人恼火。如果我们有一个目录,我们将使用shutil,它将完全支持我们想要的。
在python 3.4或更高版本中,pythonic方法是:
1 2 3 4 5
| import os
from contextlib import suppress
with suppress(OSError):
os.remove(filename) |
另一个有您自己消息的解决方案例外。
1 2 3 4 5 6
| import os
try:
os.remove(filename)
except:
print("Not able to delete the file %s" % filename) |
这是另一个解决方案:
1 2
| if os.path.isfile(os.path.join(path, filename)):
os.remove(os.path.join(path, filename)) |
亲吻礼物:
1 2 3
| def remove_if_exists(filename):
if os.path.exists(filename):
os.remove(filename) |
然后:
1
| remove_if_exists("my.file") |
- 如果你必须写一个完整的函数,它有点漏掉了一行程序的要点。
- @离子莱森的行动是在"最好"的方式来解决这个问题。如果一行程序会影响可读性,那么它永远不是更好的方法。
- 考虑到"最好"这个固有的宽泛定义,我不打算在这个意义上争论,尽管它显然受到了toctou的影响。当然不是亲吻的解决方案。
- @Matt是真的,但这里提供的许多解决方案都不受这个问题的影响吗?
像这样?利用短路评估。如果文件不存在,那么整个条件就不能为真,所以Python将不需要计算第二部分。
1
| os.path.exists("gogogo.php") and os.remove("gogogo.php") |
- 这绝对不是"更多的Python"-事实上,这是guido特别警告的,并被称为"滥用"的布尔运算符。
- 哦,我同意-部分问题是单行道,这是第一个突然出现在我脑海里的东西
- 好吧,你也可以通过在冒号后删除换行符来让它成为一行程序……或者,更好的是,guide勉强地添加了if表达式来防止人们"滥用布尔运算符",并且有一个很好的机会证明任何东西都可以被滥用:os.remove("gogogo.php")if os.path.exists("gogogo.php")else none。:)
我使用了rm,它可以强制删除不存在的--preserve-root文件作为rm的选项。
1 2
| --preserve-root
do not remove `/' (default) |
1 2
| rm --help | grep"force"
-f, --force ignore nonexistent files and arguments, never prompt |
我们也可以使用安全的RM(sudo apt-get install safe-rm)
Safe-rm is a safety tool intended to prevent the accidental deletion
of important files by replacing /bin/rm with a wrapper, which checks
the given arguments against a configurable blacklist of files and
directories that should never be removed.
首先,我检查文件夹/文件路径是否存在。这将阻止设置变量filetoremove/foldertoremoveto the stringr/`。
1 2 3 4 5 6
| 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] |
- 使用shell处理一些琐碎的事情是多余的,而且这种方法也不能跨平台(即Windows)工作。
- 你说的过度杀人是什么意思?使用shell不应该成为瓶颈。@ nabel.
- 使用shell而不是标准库(例如os.remove)始终是最不可能的方法之一。例如,您必须手动处理shell返回的错误。
- 因此使用os.system()和rm是不危险的。在第一个示例中,尝试将变量fileName设置为字符串-r /,看看会发生什么!!(实际上不要这样做)
- 我增加了我的答案来安全地使用rm,防止rm -r /。@ JonBrave
- rm -f --preserve-root不够好(--preserve-root很可能是违约)。我以-r /为例,如果是-r /home或其他什么东西呢?你可能想要rm -f -- $fileToRemove,但这不是重点。
- 那么我们就不应该在bash脚本上使用rm。
- 不是你使用它的方式,它有一个变量名(环境变量),没有引用,没有保护,不,不,不,不,不,对于这个问题,不。把不小心的人暴露给os.system('rm ...')是非常危险的,对不起。
- 我已经根据你的建议更新了我的答案,如果我使用subprocess.run()这个更新的答案会更安全吗?@ JonBrave