2009-08-28 24 views
20

Actualmente tengo un bucle que intenta encontrar un nombre de archivo sin usar agregando sufijos a una cadena de nombre de archivo. Una vez que no puede encontrar un archivo, usa el nombre que no pudo abrir un nuevo archivo con ese nombre. El problema es que este código se usa en un sitio web y puede haber múltiples intentos de hacer lo mismo al mismo tiempo, por lo que existe una condición de carrera.Cómo creo un archivo en python sin sobreescribir un archivo existente

¿Cómo puedo evitar que python sobrescriba un archivo existente, si se crea entre el momento de la comprobación y el momento de la apertura en el otro hilo?

Puedo minimizar la posibilidad de aleatorizar los sufijos, pero la posibilidad ya se ha minimizado en función de las partes de la ruta de acceso. Quiero eliminar esa posibilidad con una función que se pueda contar, crear este archivo SOLAMENTE si no existe.

Puedo usar las funciones de win32 para hacer esto, pero quiero que esto funcione en plataforma cruzada porque al final se alojará en Linux.

+0

Si tuviera que hacer algo así, usaría un nombre de archivo predefinido y anexaría la fecha/hora actual; de ese modo, se garantizará un nombre de archivo único independientemente. –

+0

La fecha está actualmente en el nombre del archivo, el problema está en un servidor web muy cargado, fácilmente podría tener 2 solicitudes en el mismo segundo. – boatcoder

+4

Use uuid.uuid1() para crear archivos con nombres globalmente únicos. – hughdbrown

Respuesta

37

Uso os.open() con os.O_CREAT y os.O_EXCL para crear el archivo. Que pueden fallar si el archivo ya existe:

>>> fd = os.open("x", os.O_WRONLY | os.O_CREAT | os.O_EXCL) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
OSError: [Errno 17] File exists: 'x' 

Una vez que haya creado un nuevo archivo, utilice os.fdopen() para girar el mango en un objeto de archivo estándar de Python:

>>> fd = os.open("y", os.O_WRONLY | os.O_CREAT | os.O_EXCL) 
>>> f = os.fdopen(fd, "w") # f is now a standard Python file object 

Editar: De Python 3.3, el built-in open() tiene un modo x que significa "abierto para creación exclusiva, fallando si el archivo ya existe".

+7

en Linux/python 2.6, 'os.fdopen (..)' arroja un 'OSError' con' errno' 22 para el ejemplo anterior porque el argumento 'mode' dejó su valor predeterminado (' 'r''). 'f = os.fdopen (fd," w ")' sin embargo funciona. –

7

Si le preocupa una condición de carrera, puede create a temporary file y luego cambiarle el nombre.

>>> import os 
>>> import tempfile 
>>> f = tempfile.NamedTemporaryFile(delete=False) 
>>> f.name 
'c:\\users\\hughdb~1\\appdata\\local\\temp\\tmpsmdl53' 
>>> f.write("Hello world") 
>>> f.close() 
>>> os.rename(f.name, r'C:\foo.txt') 
>>> if os.path.exists(r'C:\foo.txt') : 
...  print 'File exists' 
... 
File exists 

Como alternativa, puede crear los archivos con un uuid en el nombre. Stackoverflow artículo en esto.

>>> import uuid 
>>> str(uuid.uuid1()) 
'64362370-93ef-11de-bf06-0023ae0b04b8' 
+1

Estoy comprobando si existe, me preocupa una condición de carrera como se indicó anteriormente. TemporaryFile no tiene eliminar como parámetro. NamedTemporaryFile sí lo hace (en v2.6), gracias por el puntero a esta parte de la biblioteca de Python que no sabía que existía. Lo del UUID probablemente funcionaría, pero parece un poco exótico para lo que realmente necesito. – boatcoder

0

Si tiene un id asociado a cada subproceso/proceso que intenta crear el archivo, puede poner ese id en el sufijo en alguna parte, garantizando así que no hay dos procesos que puedan usar el mismo nombre de archivo.

Esto elimina la condición de carrera entre los procesos.

+0

Esto puede ser una suposición válida para sistemas de archivos locales (no conectados a la red) (en un sistema similar a UNIX). (Por supuesto, hay otras preocupaciones si el open() se puede ejecutar en versiones anteriores de NFS, Linux anterior u otros kernels del sistema operativo, etc: http://stackoverflow.com/questions/3406712/open-o-creat-o- excl-on-nfs-in-linux –

Cuestiones relacionadas