Самый питонный способ удалить файл, который может не существовать

453

Я хочу удалить файл, filenameесли он существует. Правильно ли говорить

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

Есть ли способ лучше? Однострочным способом?

Скотт К Уилсон
источник
7
Хотите ли вы попытаться удалить файл, если он существует (и потерпите неудачу, если у вас нет прав), или сделать все возможное, чтобы у вас не было ошибки?
Donal Fellows
Я хотел сделать "первое" из того, что сказал @DonalFellows. Для этого, я думаю, оригинальный код Скотта был бы хорошим подходом?
LarsH
Сделайте вызванную функцию unlinkи поместите ее в пространство имен PHP.
lama12345
1
@LarsH Смотрите второй блок кода принятого ответа. Вызывает исключение, если исключением является ошибка «нет такого файла или каталога».
jpmc26

Ответы:

613

Более питонический путь был бы:

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

Хотя это занимает еще больше строк и выглядит очень некрасиво, это позволяет избежать ненужного вызова os.path.exists()и следовать соглашению Python о чрезмерном использовании исключений.

Возможно, стоит написать функцию, которая сделает это за вас:

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
Matt
источник
17
Но пройдет ли это, если операция удаления не удалась (файловая система только для чтения или какая-то другая непредвиденная проблема)?
Скотт С. Уилсон
136
Кроме того, тот факт, что файл существует, когда os.path.exists()выполняется, не означает, что он существует, когда os.remove()выполняется.
добро пожаловать
8
Мой +1, но чрезмерное использование исключений не является соглашением Python :) Или это?
pepr
8
@pepr Я просто с юмором критиковал, что исключения являются частью нормального поведения в python. Например, итераторы должны вызывать исключения, чтобы прекратить итерации.
Мэтт
5
+1, потому что я не могу +2. Помимо того, что он более Pythonic, он на самом деле правильный, в то время как оригинал - нет, по той причине, которую предложил любезно. Подобные гоночные условия приводят к дырам в безопасности, ошибкам, которые
трудно воспроизвести
160

Я предпочитаю подавлять исключение, а не проверять наличие файла, чтобы избежать ошибки TOCTTOU . Ответ Мэтта является хорошим примером этого, но мы можем немного упростить его в Python 3, используя contextlib.suppress():

import contextlib

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

Если filenameэто pathlib.Pathобъект, а не строка, мы можем вызвать его .unlink()метод вместо использования os.remove(). По моему опыту, объекты Path более полезны, чем строки для манипулирования файловой системой.

Поскольку все в этом ответе является эксклюзивным для Python 3, это дает еще одну причину для обновления.

Kevin
источник
8
Это самый питонический способ, как в декабре 2015 года. Хотя Python продолжает развиваться.
Mayank Jaiswal
2
Я не нашел метода remove () для объектов pathlib.Path в Python 3.6
BrianHVB
1
@jeffbyrnes: Я бы назвал это нарушением дзен Python: «Должен быть один - и желательно только один - очевидный способ сделать это». Если бы у вас было два метода, которые делали одно и то же, вы в конечном итоге смешали бы их при запуске исходного кода, и читателю было бы труднее им следовать. Я подозреваю, что они хотели согласования unlink(2), которое является безусловно самым старым соответствующим интерфейсом здесь.
Кевин
1
@nivk: Если вам нужен exceptпункт, то вы должны использовать try/ except. Его нельзя существенно сократить, потому что у вас должна быть строка для ввода первого блока, сам блок, строка для ввода второго блока, а затем этот блок, поэтому try/ exceptуже настолько кратко, насколько это возможно.
Кевин
1
Стоит отметить, что в отличие от блока try / Кроме того, это решение означает, что вам не нужно возиться с созданием исключения, чтобы убедиться, что показатели тестового покрытия актуальны.
Thclark
50

os.path.existsвозвращает Trueдля папок, а также файлов. Попробуйте использовать, os.path.isfileчтобы проверить, существует ли файл вместо этого.

abought
источник
4
Каждый раз, когда мы проверяем существование и затем удаляемся на основе этого теста, мы открываем себя для состояния гонки. (Что, если файл исчезнет между ними?)
Алекс Л
34

В духе ответа Энди Джонса, как насчет подлинной троичной операции:

os.remove(fn) if os.path.exists(fn) else None
Тим Китинг
источник
41
Ужасное неправильное использование троичных.
bgusach
19
@BrianHVB Поскольку тройники существуют для выбора между двумя значениями, основанными на условии, а не на разветвлении.
bgusach
1
Я не люблю использовать исключения для управления потоком. Они затрудняют понимание кода и, что более важно, могут маскировать некоторые другие возникающие ошибки (например, проблему с разрешением, блокирующую удаление файла), которая приведет к сбою без вывода сообщений.
Эд Кинг,
11
Это не атомарно. Файл может быть удален между вызовами, чтобы существовать и удалить. Безопаснее попытаться выполнить операцию и позволить ей потерпеть неудачу.
ConnorWGarvey
1
@ nam-g-vu Просто к вашему сведению, я откатил ваши изменения, потому что вы просто добавили синтаксис исходного опрашивающего в качестве альтернативы. Так как они искали что-то другое, я не думаю, что редактирование уместно для этого конкретного ответа.
Тим Китинг
10

Начиная с Python 3.8, используйте missing_ok=Trueи pathlib.Path.unlink( документы здесь )

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
Лучший ответ для практического python3 на мой взгляд.
mrgnw
9

Другой способ узнать, существует ли файл (или файлы), и удалить его, используя глобус модуля.

from glob import glob
import os

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

Glob находит все файлы, которые могут выбрать шаблон с подстановочным знаком * nix, и зацикливает список.

jotacor
источник
6
if os.path.exists(filename): os.remove(filename)

это однострочник

Многие из вас могут не согласиться - возможно, по таким причинам, как, например, предполагаемое использование троичных растений «некрасиво», но возникает вопрос о том, следует ли нам слушать людей, привыкших к уродливым стандартам, когда они называют что-то нестандартное «некрасивым».

DevonMcC
источник
3
это чисто - я не люблю использовать исключения для управления потоком. Они затрудняют понимание кода и, что более важно, могут маскировать некоторые другие возникающие ошибки (например, проблему с разрешением, блокирующую удаление файла), которая приведет к сбою без вывода сообщений.
Эд Кинг,
2
Это не красиво, потому что предполагается, что есть только один процесс, который изменит имя файла. Это не атомно. Безопасно и правильно пытаться выполнить операцию и изящно провалиться. Раздражает, что Python не может стандартизироваться. Если бы у нас был каталог, мы бы использовали shutil, и он бы поддерживал именно то, что мы хотим.
ConnorWGarvey
2

В Python 3.4 или более поздней версии pythonic путь будет:

import os
from contextlib import suppress

with suppress(OSError):
    os.remove(filename)
Росс Кастроверде
источник
3
Это существенно не отличается от ответа, предложенного здесь .
18:00
1

Что-то вроде этого? Использует преимущества оценки короткого замыкания. Если файл не существует, все условие не может быть истинным, поэтому python не будет беспокоить оценку второй части.

os.path.exists("gogogo.php") and os.remove("gogogo.php")
Энди Джонс
источник
25
Это определенно не «более Pythonic» - на самом деле, это то, о чем Гвидо специально предупреждает и называет «злоупотреблением» булевыми операторами.
abarnert
1
о, я согласен - часть вопроса, заданная для однострочного пути, и это было первое, что пришло мне в голову
Энди Джонс
4
Ну, вы также можете сделать его однострочным, просто удалив символ новой строки после двоеточия ... Или, что еще лучше, Гид неохотно добавил выражение if, чтобы не дать людям "злоупотреблять булевыми операторами", и есть отличная возможность доказать что можно злоупотреблять чем угодно: os.remove ("gogogo.php"), если os.path.exists ("gogogo.php"), иначе нет. :)
abarnert
0

Поцелуй предложение:

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

А потом:

remove_if_exists("my.file")
Baz
источник
1
Если вам нужно написать целую функцию, она как бы пропускает точку с одним строком
Ион Лесан
@Ion Lesan ОП - это «лучший» способ решить эту проблему. Один вкладыш никогда не будет лучшим способом, если он ставит под угрозу читабельность.
Баз
Учитывая по сути широкое определение «лучший», я не собираюсь спорить в этом смысле, хотя TOCTOU явно на него влияет. И определенно не решение KISS.
Ион Лесан
@ Matt Верно, но не много ли предлагаемых здесь решений страдают от этой проблемы?
Баз
0

Это еще одно решение:

if os.path.isfile(os.path.join(path, filename)):
    os.remove(os.path.join(path, filename))
Киан
источник
0

Другое решение с вашим собственным сообщением в исключении.

import os

try:
    os.remove(filename)
except:
    print("Not able to delete the file %s" % filename)
Риши Бансал
источник
-1

Я использовал, rmкоторый может заставить удалить несуществующие файлы --preserve-rootв качестве опции rm.

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

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

Мы также можем использовать safe-rm ( sudo apt-get install safe-rm)

Safe-rm - это инструмент безопасности, предназначенный для предотвращения случайного удаления важных файлов путем замены / bin / rm оболочкой, которая проверяет заданные аргументы на предмет настраиваемого черного списка файлов и каталогов, которые никогда не следует удалять.

Сначала я проверяю, существует ли путь к папке / файлу или нет. Это предотвратит установку переменной 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]
Alper
источник
1
Использование оболочки для чего-то этого тривиально излишне, и этот подход также не будет работать кроссплатформенно (то есть Windows).
Набла
4
Использование оболочки вместо стандартной библиотеки (например, os.remove) всегда является одним из наименее питонных / чистых способов сделать что-либо. Например, вы должны вручную обрабатывать ошибки, возвращаемые оболочкой.
Набла
1
Я добавил свой ответ, чтобы использовать rmбезопасно и предотвратить rm -r /. @JonBrave
Alper
1
rm -f --preserve-rootне достаточно хорош ( --preserve-rootвероятно, по умолчанию в любом случае). Я привел -r / в качестве примера , что если это -r /homeили что-то еще? Вы, вероятно, хотите rm -f -- $fileToRemove, но это не главное.
JonBrave
3
Не так, как вы использовали, с именем переменной (переменной среды), без кавычек и без защиты, нет. И не по этому вопросу, нет. Неосторожное os.system('rm ...')подвергание крайне опасно, извините.
JonBrave