Найбільш пітонічний спосіб видалити файл, який може не існувати


453

Я хочу видалити файл, filenameякщо він існує. Чи правильно сказати

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

Чи є кращий спосіб? Однорядковий спосіб?


7
Ви хочете спробувати видалити файл, якщо він існує (і не вдалося, якщо вам не вистачає дозволів), або зробити все можливе, щоб видалити файл і ніколи не матиме помилки назад у обличчя?
стипендіати

Я хотів зробити "колишнє" того, що сказав @DonalFellows. Для цього, я думаю, оригінальний код Скотта був би хорошим підходом?
LarsH

Створіть функцію, що викликається, unlinkі помістіть її в область імен PHP.
lama12345

1
@LarsH Дивіться другий блок коду прийнятої відповіді. Він повторно підкреслює виняток, якщо виняток - це щось, крім помилки "немає такого файлу чи каталогу".
jpmc26

Відповіді:


613

Більш пітонічним способом було б:

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

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

Можливо, варто написати функцію для цього:

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
Але чи пройде це, якщо операція видалення не вдалася (читання лише файлової системи чи інша несподівана проблема)?
Скотт C Вілсон

137
Також той факт, що файл існує при os.path.exists()виконанні, не означає, що він існує, коли os.remove()виконується.
kindall

8
Мій +1, але надмірне використання винятків не є умовою Python :) Або це?
pepr

8
@pepr Я просто жартівливо критикував, як винятки є частиною нормальної поведінки в python. Наприклад, ітератори повинні збільшити винятки, щоб зупинити ітерацію.
Мет

5
+1, тому що я не можу +2. Окрім того, що є більш пітонічним, цей насправді правильний, тоді як оригінал - ні, з тієї причини, яку добродушно запропонував. Такі умови для перегонів призводять до ям у безпеці, важко
репресованих

160

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

import contextlib

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

Якщо filenameце pathlib.Pathоб'єкт замість рядка, ми можемо викликати його .unlink()метод замість використання os.remove(). На мій досвід, об'єкти Path корисніші за рядки для маніпуляцій з файловою системою.

Оскільки все у цій відповіді виключно для Python 3, це ще одна причина для оновлення.


8
Це найбільш пітонічний спосіб, як на грудень 2015 року. Пітон продовжує розвиватися.
Mayank Jaiswal

2
Я не знайшов методу remove () для pathlib.Path об'єктів на Python 3.6
BrianHVB

1
@jeffbyrnes: Я б назвав порушенням дзен Python: "Має бути один - і бажано лише один - очевидний спосіб зробити це". Якби у вас було два способи, які зробили те саме, ви закінчилися їх сумішшю у виконанні вихідного коду, який читачеві було б важче дотримуватися. Я підозрюю, що вони хотіли узгодженості unlink(2), що є, безумовно, найстарішим відповідним інтерфейсом тут.
Кевін

1
@nivk: Якщо вам потрібна exceptстаття, ви повинні використовувати try/ except. Це не може бути змістовно скороченим, тому що ви повинні мати рядок для введення першого блоку, сам блок, рядок для введення другого блоку, а потім цей блок, так що try/ exceptвже є максимально стислим.
Кевін

1
Варто зазначити, що на відміну від блоку спробу / за винятком блоку, це рішення означає, що вам не доведеться возитися зі створенням винятку, щоб забезпечити релевантність показників тестового покриття.
thclark

50

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


4
Кожен раз, коли ми перевіряємо існування, а потім видаляємо на основі цього тесту, ми відкриваємося до перегонів. (Що робити, якщо файл зникає між ними?)
Alex L

34

У дусі відповіді Енді Джонса, як щодо справжньої потрійної операції:

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

41
Потворне зловживання тернаріями.
bgusach

19
@BrianHVB Оскільки тернарі є, щоб вибирати між двома значеннями на основі умови, а не робити розгалуження.
bgusach

1
Я не люблю використовувати винятки для контролю потоку. Вони роблять код важким для розуміння, і, що ще важливіше, можуть замаскувати деякі інші помилки (наприклад, питання дозволу, що блокує видалення файлу), що спричинить мовчазну помилку.
Ед Кінг

11
Це не атомно. Файл можна видалити між існуючими дзвінками та видалити їх. Безпечніше спробувати операцію і дозволити її відмовити.
ConnorWGarvey

1
@ nam-g-vu Просто FYI, я відмовив вашу редакцію, оскільки ви в основному просто додали оригінальний синтаксис запитувача як альтернативу. Оскільки вони шукали щось інше, ніж це, я не відчуваю, що редагування відповідає цій конкретній відповіді.
Тім Кітінг,

11

Станом на 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()

1
Найкраща відповідь для практичного python3 на мій погляд.
mrgnw

9

Ще один спосіб дізнатися, чи існує файл (або файли), і видалити його, використовуючи модуль glob.

from glob import glob
import os

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

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



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

є однолінійним.

Багато з вас можуть не погодитися - можливо, з таких причин, як, наприклад, вважати запропоноване використання тернаріїв "потворним", - але це ставить питання про те, чи слід слухати людей, звикли до потворних стандартів, коли вони називають щось нестандартне "потворне".


3
це чисто - я не люблю використовувати винятки для контролю потоку. Вони роблять код важким для розуміння, і, що ще важливіше, можуть замаскувати деякі інші помилки (наприклад, питання дозволу, що блокує видалення файлу), що спричинить мовчазну помилку.
Ед Кінг

2
Це не дуже, тому що він передбачає, що існує лише один процес, який змінить ім'я файлу. Це не атомне. Це безпечно і правильно, щоб спробувати операцію і вийти з ладу витончено. Прикро, що Python не може стандартизувати. Якби у нас був каталог, ми використовували б shutil, і він би підтримував саме те, що ми хочемо.
ConnorWGarvey


1

Щось на зразок цього? Користується оцінкою короткого замикання. Якщо файл не існує, весь умовний не може бути істинним, тому python не буде турбувати оцінку другої частини.

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

25
Це, безумовно, не "більше пітонічно" - адже це Guido спеціально попереджає і позначається як "зловживання" булевими операторами.
abarnert

1
о, я згоден - частина запитання задала напрям в одну лінію, і це було перше, що впало мені в голову
Енді Джонс

4
Ну, ви також можете зробити його однолінійним, просто видаливши новий рядок після товстої кишки ... Або, що ще краще, Керівництво з зухвалим додаванням вираз "if", щоб запобігти людям "зловживати булевими операторами", і є чудова можливість довести що будь-чим можна зловживати: os.remove ("gogogo.php"), якщо os.path.exists ("gogogo.php") інше Немає. :)
abarnert

0

Пропозиція KISS:

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

І потім:

remove_if_exists("my.file")

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 /папкуToRemove 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
Використовувати оболонку для чогось цього дрібниці є надмірним, і такий підхід також не працюватиме міжплатформенним (тобто 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.