A maneira mais pitônica de excluir um arquivo que pode não existir

453

Quero excluir o arquivo, filenamese existir. É apropriado dizer

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

Existe uma maneira melhor? Uma maneira de uma linha?

Scott C Wilson
fonte
7
Deseja tentar excluir um arquivo, se ele existir (e falhar se você não tiver permissões), ou fazer uma exclusão com o máximo de esforço e nunca receber um erro na sua cara?
Donal Fellows
Eu queria fazer "o primeiro" do que o @DonalFellows disse. Por isso, acho que o código original de Scott seria uma boa abordagem?
Larsh
Crie uma função chamada unlinke coloque-a no espaço para nome PHP.
Lama12345
1
@LarsH Veja o segundo bloco de código da resposta aceita. Ele gera novamente a exceção se a exceção for qualquer coisa, exceto um erro "sem esse arquivo ou diretório".
Jpmc26

Respostas:

613

Uma maneira mais pitônica seria:

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

Embora isso exija ainda mais linhas e pareça muito feio, evita a chamada desnecessária os.path.exists()e segue a convenção python de excessivas exceções.

Pode valer a pena escrever uma função para fazer isso por você:

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
fonte
17
Mas isso passaria se a operação de remoção falhasse (sistema de arquivos somente leitura ou outro problema inesperado)?
Scott C Wilson
135
Além disso, o fato de o arquivo existir quando os.path.exists()é executado não significa que ele existe quando os.remove()é executado.
kindall 31/05
8
Meu +1, mas o uso excessivo de exceções não é uma convenção do Python :) Ou é?
pepr 31/05
8
@pepr Eu estava criticando humoristicamente como as exceções fazem parte do comportamento normal em python. Por exemplo, os iteradores devem gerar exceções para interromper a iteração.
Matt
5
+1 porque não posso +2. Além de ser mais pitonico, este é realmente correto, enquanto o original não é, pelo motivo sugerido por todos. Condições de corrida como que levam a falhas de segurança, bugs difíceis de repro, etc.
abarnert
159

Prefiro suprimir uma exceção em vez de verificar a existência do arquivo, para evitar um bug do TOCTTOU . A resposta de Matt é um bom exemplo disso, mas podemos simplificá-lo um pouco no Python 3, usando contextlib.suppress():

import contextlib

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

Se filenameé um pathlib.Pathobjeto em vez de uma string, podemos chamar seu .unlink()método em vez de usar os.remove(). Na minha experiência, os objetos Path são mais úteis que as strings para manipulação do sistema de arquivos.

Como tudo nesta resposta é exclusivo do Python 3, ele fornece mais um motivo para a atualização.

Kevin
fonte
7
Essa é a maneira mais pitônica de dezembro de 2015. Embora o Python continue evoluindo.
Mayank Jaiswal
2
I encontrado nenhum método de remoção () para pathlib.Path objetos em Python 3,6
BrianHVB
1
@jeffbyrnes: Eu chamaria isso de violação do Zen de Python: "Deveria haver uma - e de preferência apenas uma - maneira óbvia de fazer isso". Se você tivesse dois métodos que fizeram a mesma coisa, você acabaria com uma mistura deles na execução do código-fonte, o que seria mais difícil para o leitor seguir. Eu suspeito que eles queriam consistência com unlink(2), que é de longe a interface relevante mais antiga aqui.
Kevin
1
@nivk: Se você precisar de uma exceptcláusula, deverá usar try/ except. Ele não pode ser significativamente reduzido, porque você deve ter uma linha para introduzir o primeiro bloco, o bloco em si, uma linha para introduzir o segundo bloco, e depois que o bloco, de modo try/ exceptjá é tão concisa quanto possível.
Kevin Kevin
1
Vale ressaltar que, diferentemente de um bloco try / except, essa solução significa que você não precisa mexer na criação de uma exceção para garantir que as métricas de cobertura de teste sejam relevantes.
Thdark #
50

os.path.existsretorna Truepara pastas e arquivos. Considere usar os.path.isfilepara verificar se o arquivo existe.

abought
fonte
4
Sempre que testamos a existência e removemos com base nesse teste, estamos nos abrindo para uma condição de corrida. (E se o arquivo desaparece no meio?)
Alex L
34

No espírito da resposta de Andy Jones, que tal uma autêntica operação ternária:

os.remove(fn) if os.path.exists(fn) else None
Tim Keating
fonte
41
Uso indevido feio de ternários.
bgusach 10/09/2015
19
@BrianHVB Como existem ternários para escolher entre dois valores com base em uma condição, não para ramificação.
bgusach
1
Não gosto de usar exceções para controle de fluxo. Eles dificultam o entendimento do código e, mais importante, podem mascarar outros erros (como um problema de permissão que bloqueia a exclusão de um arquivo) que causam uma falha silenciosa.
Ed King
11
Isso não é atômico. O arquivo pode ser excluído entre as chamadas para existir e remover. É mais seguro tentar a operação e permitir que ela falhe.
ConnorWGarvey
1
@ nam-g-vu Recentemente, revirei sua edição porque você basicamente adicionou a sintaxe do questionador original como alternativa. Como eles procuravam algo diferente, não acho que a edição seja pertinente a essa resposta específica.
Tim Keating
9

Outra maneira de saber se o arquivo (ou arquivos) existe e removê-lo é usando o módulo glob.

from glob import glob
import os

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

O Glob encontra todos os arquivos que poderiam selecionar o padrão com um curinga * nix e faz um loop na lista.

jotacor
fonte
9

No Python 3.8, use missing_ok=Truee pathlib.Path.unlink( docs aqui )

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
fonte
1
Melhor resposta para python3 prático na minha opinião.
mrgnw
7

A resposta de Matt é o caminho certo para Pythons mais velhos e Kevin é a resposta certa para os mais novos.

Se você não deseja copiar a função silentremove, essa funcionalidade é exposta em path.py como remove_p :

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

é um one-liner.

Muitos de vocês podem discordar - possivelmente por razões como considerar o uso proposto de ternários "feio" - mas isso levanta a questão de saber se devemos ouvir as pessoas acostumadas a padrões feios quando chamam algo não-padrão de "feio".

DevonMcC
fonte
3
isso é limpo - não gosto de usar exceções para controle de fluxo. Eles dificultam o entendimento do código e, mais importante, podem mascarar outros erros (como um problema de permissão que bloqueia a exclusão de um arquivo) que causam uma falha silenciosa.
Ed King
2
Não é bonito porque assume que há apenas um processo que modificará o nome do arquivo. Não é atômico. É seguro e correto tentar a operação e falhar normalmente. É irritante que o Python não possa padronizar. Se tivéssemos um diretório, usaríamos shutil e suportaria exatamente o que queremos.
ConnorWGarvey
2

No Python 3.4 ou versão posterior, a maneira pitônica seria:

import os
from contextlib import suppress

with suppress(OSError):
    os.remove(filename)
Ross Castroverde
fonte
3
Isso não difere substancialmente da resposta oferecida aqui .
CHB
1

Algo assim? Aproveita a avaliação de curto-circuito. Se o arquivo não existir, toda a condicional não poderá ser verdadeira; portanto, o python não incomodará a avaliação da segunda parte.

os.path.exists("gogogo.php") and os.remove("gogogo.php")
Andy Jones
fonte
25
Definitivamente, isso não é "mais pitonico" - na verdade, é algo sobre o qual Guido especificamente alerta e se refere como "abuso" dos operadores booleanos.
abarnert
1
oh, eu concordo - parte da pergunta pedia uma via única e essa foi a primeira coisa que me veio à cabeça
Andy Jones
4
Bem, você também pode torná-lo uma linha única removendo a nova linha após os dois pontos ... Ou, melhor ainda, o Guide acrescentou de má vontade a expressão if para impedir as pessoas de "abusarem dos operadores booleanos", e há uma grande oportunidade para provar que qualquer coisa pode ser abusada: os.remove ("gogogo.php") se os.path.exists ("gogogo.php") else Nenhum. :)
abarnert
0

Uma oferta do KISS:

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

E depois:

remove_if_exists("my.file")
Baz
fonte
1
Se você tem que escrever uma função todo, tipo de perde o ponto de one-liners
Ion Lesan
@Ion Lesan O OP está atrás da "melhor" maneira de resolver esse problema. Um liner nunca é a melhor maneira se comprometer a legibilidade.
Baz
Dada a definição inerentemente ampla de "melhor", não vou discutir neste sentido, embora seja claramente afetado pelo TOCTOU. E definitivamente não é uma solução do KISS.
Ion Lesan
@ Matt True, mas várias soluções oferecidas aqui não sofrem com esse problema?
Baz
0

Esta é outra solução:

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

Outra solução com sua própria mensagem em exceção.

import os

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

Eu usei o rmque pode forçar a excluir arquivos inexistentes com --preserve-rootcomo uma opção para rm.

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

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

Também podemos usar safe-rm ( sudo apt-get install safe-rm)

O Safe-rm é uma ferramenta de segurança destinada a impedir a exclusão acidental de arquivos importantes, substituindo / bin / rm por um wrapper, que verifica os argumentos fornecidos em uma lista negra configurável de arquivos e diretórios que nunca devem ser removidos.

Primeiro, verifico se o caminho da pasta / arquivo existe ou não. Isso impedirá a configuração da variável 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
fonte
1
Usar um shell para algo que é trivial é um exagero e essa abordagem também não funciona em várias plataformas (por exemplo, Windows).
Nabla
4
Usar um shell em vez da biblioteca padrão (os.remove por exemplo) é sempre uma das maneiras menos pitônicas / limpas de fazer alguma coisa. Por exemplo, você precisa manipular manualmente os erros retornados pelo shell.
Nabla
1
Eu adicionei minha resposta para usar rmcom segurança e evitar rm -r /. @JonBrave
alper
1
rm -f --preserve-rootnão é bom o suficiente ( --preserve-rootprovavelmente é o padrão de qualquer maneira). Dei -r / como exemplo , e se for -r /homeou o que for? Você provavelmente quer rm -f -- $fileToRemove, mas esse não é o ponto.
JonBrave
3
Não da maneira que você a usou, com um nome de variável (variável de ambiente) e sem aspas e sem proteção. E não para esta pergunta, não. Expor os incautos os.system('rm ...')é extremamente perigoso, desculpe.
JonBrave