La forma más pitónica de eliminar un archivo que puede no existir

453

Quiero eliminar el archivo filenamesi existe. ¿Es correcto decir

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

¿Hay una mejor manera? ¿Un camino de una línea?

Scott C Wilson
fuente
77
¿Desea intentar eliminar un archivo si existe (y falla si carece de permisos) o hacer una eliminación de gran esfuerzo y nunca se le devolverá un error?
Donal Fellows
Quería hacer "lo primero" de lo que dijo @DonalFellows. Para eso, supongo que el código original de Scott sería un buen enfoque.
LarsH
Haga una función llamada unlinky colóquela en el espacio de nombres PHP.
lama12345
1
@LarsH Vea el segundo bloque de código de la respuesta aceptada. Vuelve a generar la excepción si la excepción es cualquier cosa menos un error de "no existe tal archivo o directorio".
jpmc26

Respuestas:

613

Una forma más pitónica sería:

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

Aunque esto toma aún más líneas y se ve muy feo, evita la llamada innecesaria os.path.exists()y sigue la convención de Python de sobreutilizar excepciones.

Puede valer la pena escribir una función para hacer esto por usted:

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
Mate
fuente
17
Pero, ¿pasaría esto si fallara la operación de eliminación (sistema de archivos de solo lectura o algún otro problema inesperado)?
Scott C Wilson
136
Además, el hecho de que el archivo existe cuando os.path.exists()se ejecuta no significa que exista cuando os.remove()se ejecuta.
poco
8
Mi +1, pero el uso excesivo de excepciones no es una convención de Python :) ¿O sí?
pepr
8
@pepr Estaba criticando humorísticamente cómo las excepciones son parte del comportamiento normal en Python. Por ejemplo, los iteradores deben generar excepciones para dejar de iterar.
Matt
55
+1 porque no puedo +2. Además de ser más pitónico, este es realmente correcto, mientras que el original no lo es, por la razón sugerida. Las condiciones de carrera como el que llevan a los agujeros de seguridad, errores difíciles de repro, etc.
abarnert
160

Prefiero suprimir una excepción en lugar de verificar la existencia del archivo, para evitar un error TOCTTOU . La respuesta de Matt es un buen ejemplo de esto, pero podemos simplificarlo ligeramente en Python 3, usando contextlib.suppress():

import contextlib

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

Si filenamees un pathlib.Pathobjeto en lugar de una cadena, podemos llamar a su.unlink() método en lugar de usar os.remove(). En mi experiencia, los objetos Path son más útiles que las cadenas para la manipulación del sistema de archivos.

Como todo en esta respuesta es exclusivo de Python 3, proporciona otra razón más para actualizar.

Kevin
fuente
8
Esta es la forma más pitónica en diciembre de 2015. Sin embargo, Python sigue evolucionando.
Mayank Jaiswal
2
No encontré ningún método remove () para pathlib.Path objetos en Python 3.6
BrianHVB
1
@jeffbyrnes: Yo llamaría a eso una violación del Zen de Python: "Debería haber una, y preferiblemente solo una, forma obvia de hacerlo". Si tuviera dos métodos que hicieran lo mismo, terminaría con una mezcla de ellos al ejecutar el código fuente, lo que sería más difícil de seguir para el lector. Sospecho que querían coherencia con unlink(2), que es, con mucho, la interfaz relevante más antigua aquí.
Kevin
1
@nivk: Si necesita una exceptcláusula, entonces debe usar try/ except. No se puede acortar significativamente, porque debe tener una línea para introducir el primer bloque, el bloque en sí, una línea para introducir el segundo bloque, y luego ese bloque, por lo que try/ exceptes lo más breve posible.
Kevin
1
Vale la pena señalar que, a diferencia de un bloque try / except, esta solución significa que no tiene que perder el tiempo creando una excepción para garantizar que las métricas de cobertura de prueba sean relevantes.
Thclark
50

os.path.existsdevuelve Truepara carpetas y archivos. Considere usar os.path.isfilepara verificar si el archivo existe en su lugar.

abought
fuente
44
Cada vez que probamos la existencia y luego la eliminamos en función de esa prueba, nos estamos abriendo a una condición de carrera. (¿Qué pasa si el archivo desaparece en el medio?)
Alex L
34

En el espíritu de la respuesta de Andy Jones, ¿qué tal una auténtica operación ternaria:

os.remove(fn) if os.path.exists(fn) else None
Tim Keating
fuente
41
Mal uso feo de los ternarios.
bgusach
19
@BrianHVB Debido a que los ternaries existen para elegir entre dos valores en función de una condición, no para hacer ramificaciones.
bgusach
1
No me gusta usar excepciones para el control de flujo. Hacen que el código sea difícil de entender y, lo que es más importante, pueden enmascarar algún otro error (como un problema de permiso que bloquea la eliminación de un archivo) que provocará un error silencioso.
Ed King
11
Esto no es atómico. El archivo se puede eliminar entre llamadas a existe y eliminar. Es más seguro intentar la operación y permitir que falle.
ConnorWGarvey
1
@ nam-g-vu Solo para tu información, revertí tu edición porque básicamente solo agregaste la sintaxis del interrogador original como alternativa. Como estaban buscando algo diferente a eso, no creo que la edición sea pertinente para esta respuesta en particular.
Tim Keating
10

A partir de Python 3.8, use missing_ok=Truey pathlib.Path.unlink( documentos aquí )

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
fuente
1
La mejor respuesta para python3 práctico en mi opinión.
mrgnw
9

Otra forma de saber si el archivo (o archivos) existe, y eliminarlo, es usando el módulo glob.

from glob import glob
import os

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

Glob encuentra todos los archivos que podrían seleccionar el patrón con un comodín * nix y recorre la lista.

jotacor
fuente
7

La respuesta de Matt es la correcta para Pythons mayores y Kevin es la respuesta correcta para los más nuevos.

Si no desea copiar la función silentremove, esta funcionalidad se expone en path.py como remove_p :

from path import Path
Path(filename).remove_p()
Jason R. Coombs
fuente
6
if os.path.exists(filename): os.remove(filename)

es un trazador de líneas.

Muchos de ustedes pueden estar en desacuerdo, posiblemente por razones como considerar el uso propuesto de ternar "feo", pero esto plantea la pregunta de si debemos escuchar a las personas acostumbradas a los estándares feos cuando llaman a algo no estándar "feo".

DevonMcC
fuente
3
esto está limpio: no me gusta usar excepciones para el control de flujo. Hacen que el código sea difícil de entender y, lo que es más importante, pueden enmascarar algún otro error que ocurra (como un problema de permiso que bloquea la eliminación de un archivo) que provocará una falla silenciosa.
Ed King
2
No es bonito porque supone que solo hay un proceso que modificará el nombre de archivo. No es atómico. Es seguro y correcto intentar la operación y fallar con gracia. Es molesto que Python no pueda estandarizar. Si tuviéramos un directorio, usaríamos shutil y admitiría exactamente lo que queremos.
ConnorWGarvey
2

En Python 3.4 o versión posterior, la forma pitónica sería:

import os
from contextlib import suppress

with suppress(OSError):
    os.remove(filename)
Ross Castroverde
fuente
3
Esto no difiere sustancialmente de la respuesta ofrecida aquí .
chb
1

¿Algo como esto? Aprovecha la evaluación de cortocircuito. Si el archivo no existe, todo el condicional no puede ser verdadero, por lo que Python no molestará la evaluación de la segunda parte.

os.path.exists("gogogo.php") and os.remove("gogogo.php")
Andy Jones
fuente
25
Esto definitivamente no es "más pitónico", de hecho, es algo de lo que Guido advierte específicamente, y se refiere como "abuso" de los operadores booleanos.
abarnert
1
oh, estoy de acuerdo - parte de la pregunta fue de una línea y esto fue lo primero que se me ocurrió
Andy Jones
44
Bueno, también podría convertirlo en una frase simplemente quitando la nueva línea después del colon ... O, mejor aún, Guide agregó de mala gana la expresión if para evitar que las personas "abusen de los operadores booleanos", y hay una gran oportunidad para demostrar que cualquier cosa puede ser abusada: os.remove ("gogogo.php") si os.path.exists ("gogogo.php") más. Ninguno. :)
abarnert
0

Una oferta de KISS:

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

Y entonces:

remove_if_exists("my.file")
Baz
fuente
1
Si tiene que escribir una función completa, pierde el sentido de una frase
Ion Lesan
@Ion Lesan El OP busca la "mejor" forma de resolver este problema. Un trazador de líneas nunca es una mejor manera si pone en peligro la legibilidad.
Baz
Dada la definición inherentemente amplia de "mejor", no voy a discutir en este sentido, aunque está claramente afectado por TOCTOU. Y definitivamente no es una solución KISS.
Ion Lesan
@Matt Verdadero, pero ¿algunas de las soluciones que se ofrecen aquí no sufren este problema?
Baz
0

Esta es otra solución:

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

Otra solución con su propio mensaje en excepción.

import os

try:
    os.remove(filename)
except:
    print("Not able to delete the file %s" % filename)
Rishi Bansal
fuente
-1

He utilizado lo rmque puede forzar la eliminación de archivos inexistentes con --preserve-rootuna opción rm.

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

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

También podemos usar safe-rm ( sudo apt-get install safe-rm)

Safe-rm es una herramienta de seguridad destinada a evitar la eliminación accidental de archivos importantes al reemplazar / bin / rm con un contenedor, que verifica los argumentos dados contra una lista negra configurable de archivos y directorios que nunca deberían eliminarse.

Primero verifico si la ruta de la carpeta / archivo existe o no. Esto evitará establecer la variable 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
fuente
1
Usar un shell para algo tan trivial es excesivo y este enfoque tampoco funcionará multiplataforma (es decir, Windows).
Nabla
44
Usar un shell en lugar de la biblioteca estándar (os.remove, por ejemplo) es siempre una de las formas menos pitónicas / limpias de hacer algo. Por ejemplo, debe manejar manualmente los errores devueltos por el shell.
Nabla
1
Agregué mi respuesta para usar de rmforma segura y prevenir rm -r /. @JonBrave
Alper
1
rm -f --preserve-rootno es lo suficientemente bueno ( --preserve-rootprobablemente sea el valor predeterminado de todos modos). Di -r / como ejemplo , ¿y si es -r /homeo lo que sea? Probablemente quieras rm -f -- $fileToRemove, pero ese no es el punto.
JonBrave
3
No de la forma en que lo usó, con un nombre de variable (variable de entorno), y sin comillas, y sin protección, no. Y no para esta pregunta, no. Exponer a los incautos os.system('rm ...')es extremadamente peligroso, lo siento.
JonBrave