Listar renombrar y borrar ficheros en python

De ChuWiki

El módulo os que viene por defecto con python nos permite interactuar con el sistema operativo. Veamos en este tutorial las funciones que tiene para el manejo de ficheros. Cosas como ver qué ficheros hay en un directorio, cambiar el nombre de ficheros y directorios, crear directorios, borrar ficheros, etc.

Módulo os[editar]

Lo primero, ¿cómo no?, es importar el módulo os para tener sus funciones accesibles

>>> import os

Listado de ficheros de un directorio: listdir() y scandir()[editar]

La función listdir() devuelve un listado de nombres de ficheros y directorios en el directorio actual de trabajo. Admite como parámetro opcional el directorio que queremos listar, en vez de el actual

>>> os.listdir()
['directorio1', 'fichero1.txt', 'fichero2.txt']

>>> os.listdir('C:\\Users\\fjabe\\Proyectos\\pruebas-python')
['directorio1', 'fichero1.txt', 'fichero2.txt']

>>> os.listdir('C:/Users/fjabe/Proyectos/pruebas-python')
['directorio1', 'fichero1.txt', 'fichero2.txt']

En estos ejemplos el path que pasamos como parámetro es el mismo que el del directorio actual de trabajo, por eso el resultado es igual con o sin parámetro. Vemos también que, en el caso de windows, podemos poner las barras indistintamente como \\ o como /, funciona igual.

La funión scandir es más eficiente que listdir() y da más información sobre el fichero. Devuelve un iterador con estructuras os.DirEntry y estas tiene bastante información sobre el fichero

>>> entradas = os.scandir()
>>> for entrada in entradas:
...    print(entrada)
...
<DirEntry 'directorio1'>
<DirEntry 'fichero1.txt'>
<DirEntry 'fichero2.txt'>

os.DirEntry es una clase. Tiene bastantes parámetros que puedes consultar. En la documentación oficial de os.DirEntry tienes un listado completo. Aquí vamos a ver sólo algunos de ellos, los que pueden ser más útiles

>>> entradas = os.scandir()
>>> entrada = next(entradas)

>>> entrada.name
'directorio1'
>>> entrada.path
'.\\directorio1'
>>> entrada.is_dir()
True
>>> entrada.is_file()
False
>>> entrada.is_symlink()
False
>>> entrada.stat()
os.stat_result(st_mode=16895, st_ino=0, st_dev=0, st_nlink=0, st_uid=0, st_gid=0, st_size=0, st_atime=1670262234, st_mtime=1670262234, st_ctime=1670262234)

Los atributos/funciones que miramos:

  • name devuelve el nombre de la entrada (fichero o directorio)
  • path devuelve el path al fichero concatenando lo que hayamos pasado como parámetro en scandir(). Como no hemos puesto parámetro, por defecto es el directorio actual ".".
  • is_dir() devuelve True si el fichero es un directorio o un link simbólico a un directorio, False en caso contrario. Admite como paráemtro opcional follow_symlinks que por defecto es True. En linux existe el concepto de link simbólicos y en windows el concepto de acceso directo. Básicamente es un enlace a un directorio o fichero real que está en otra ubicación. Dicho de otra forma, en el directorio A puedo tener algo que se parece a un fichero F, pero que en realidad es un enlace simbólico al fichero F que realmente está en un directorio B. La llamda is_dir() por defecto tiene follow_symlinks a True, así que devuelve True tanto si el fichero es un directorio como si es un enlace simbólico a un directorio. Si en la llamada a is_dir() decimos que no siga follow_symlinks, devolverá True sólo si el fichero es un directorio real, no si es un enlace simbólico
  • is_file() devuelve True si la entrada es un fichero o un enlace simbólico a un fichero. Al igual que is_dir(), admite un parámetro opcional follow_symlinks con el mismo significado.
  • is_symlink() devuelve True si la entrada es un link simbólico, False en caso contrario.
  • stat() devuelve una serie de estadísticas del fichero en una estructura stat_result. Son interesantes los siguientes campos:
    • st_size da el tamaño en bytes del fichero
    • st_atime es la fecha/hora del último acceso al fichero. Va en segundos desde el 1 de Enero de 1970. Puedes ver más detalles sobre el tiempo en 10 - Curso de Python - Datetime
    • st_mtime es la fecha/hora de la última modificación del fichero.
    • st_ctime es la fecha/hora de creación del fichero (en windows) o en que se modificaron sus metadatos (en linux)

Si nos interesan estas estadísticas de un fichero concreto, podemos usar la función stat() directamente sin necesidad de llamar a scandir()

>>> os.stat("fichero1.txt")
os.stat_result(st_mode=33206, st_ino=117938015241904975, st_dev=3699096245, st_nlink=1, st_uid=0, st_gid=0, st_size=20, st_atime=1670264144, st_mtime=1670264143, st_ctime=1670262215)

Borrado de un fichero: remove() y unlink()[editar]

Las funciones remove() y unlink() hacen lo mismo. unlink() es el nombre tradicional en linux. Borran el fichero que se les pasa. Si se le pasa un directorio da error. Para borrar directorios se debe usar la función rmdir() que se explica más adelante.

>>> os.listdir()
['directorio1', 'fichero1.txt', 'fichero2.txt']

>>> os.remove('fichero1.txt')
>>> os.listdir()
['directorio1', 'fichero2.txt']

Cambiar el nombre de un fichero o directorio: rename(), renames() y replace()[editar]

La función rename() cambia el nombre de un ficheo o directorio.

>>> os.listdir()
['directorio1', 'fichero1.txt', 'fichero2.txt']
>>> os.rename('fichero1.txt', 'nuevo_fichero1.txt')
>>> os.listdir()
['directorio1', 'fichero2.txt', 'nuevo_fichero1.txt']

La función renames() puede actuar sobre path completos de directorios y subdirectorios. Crea los nuevos que hagan falta y elimina los antiguos si están vacíos.

>>> os.listdir()
['directorio1', 'fichero1.txt', 'fichero2.txt', 'fichero3.txt', 'path1']
>>> os.listdir('path1')
['fichero3.txt']

>>> os.renames('path1/fichero3.txt','path2/fichero4.txt')

>>> os.listdir()
['directorio1', 'fichero1.txt', 'fichero2.txt', 'path2']
>>> os.listdir('path2')
['fichero4.txt']

En el ejemplo, primer bloque. Vemos la situación actual: Hay un directorio path1 que tiene dentro un fichero3.txt. Llamamos a la función renames() para llevar ese fichero a otro directorio distinto. En la situación final vemos que path1 ha desaparecido y que se ha creado path2. path1 ha desaparecido porque se había quedado vacío al mover el fichero. El nuevo fichero con su nuevo nombre etá dentro de path2.

replace() hace lo mismo que la función rename() pero con una diferencia. rename() falla si el fichero de destino existe. Con replace() lo machacamos.

>>> os.listdir()
['directorio1', 'fichero1.txt', 'fichero2.txt']

>>> os.rename('fichero1.txt','fichero2.txt')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
FileExistsError: [WinError 183] No se puede crear un archivo que ya existe: 'fichero1.txt' -> 'fichero2.txt'

>>> os.replace('fichero1.txt','fichero2.txt')
>>> os.listdir()
['directorio1', 'fichero2.txt']

Vemos que rename() ha fallado porque el fichero de destino ya existe. replace() machaca el fichero. El comportamiento tanto de uno como de otro puede depender del sistema operativo, por lo qu eno está de más verificar si el fichero de destino existe o no antes de intentar cambiar de nombre el primero.

Manejo de directorios: mkdir(), makedirs(), rmdir() y removedirs()[editar]

mkdir() permite crear un directorio nuevo.

>>> os.mkdir("directorio2")
>>> os.listdir()
['directorio1', 'directorio2', 'fichero1.txt', 'fichero2.txt']

Se puede añadir un parámetro mode con los permisos para el directorio, expresado como un número. La forma fácil de escribir el número es en octal. Cada cifra ocupa 3 bits y cada bit representa un posible permiso (read, write, ejecución). read es permiso para leer en el directorio, write es permiso para escribir en el directorio. ejecución es permiso para ejecutar programas que estén en el directorio. El directorio lleva tres bloques de estos permisos, es decir, 9 bits en total. Los tres primeros son para el propietario del directorio (el que lo ha creado). Los tres siguientes son para los usuarios que pertenecen al mismo grupo del usuario que lo ha creado. Y los tres últimos bits de permisos son para usuarios que no son ni el propietario ni están el el grupo del propietario. Todo esto cobra mucho sentido en linux, no tanto wn windows.

>>> os.mkdir("directorio2", 0o751)

Veamos con un poco de detalle el número de permisos

  • 0o es la forma en python de decir que el número que va detrás está en octal. Como comentamos, en octal cada cifra son tres bits, así que 751 son 9 bits en total.
  • 7 son los permisos que queremos para el propietario del directorio. Nosotros que somos los que lo hemos creado. 7 en binario es 111, correspondiente a los permisos rwx (read, write, ejecución). Como todos están a 1, tenemos permiso para leer, escribir y ejecutar programas en ese directorio.
  • 5 son los permisos que queremos para usuarios que pertenezcan al mismo grupo que el propietario. 5 en binario es 101, es decir, tendrán permisos de lectura y de ejecución, pero no de escritura.
  • 1 son los permisos para usuarios que no sean el propietario ni pertenezcan al grupo del propietario. 1 en binario es 001, es decir, sólo tiene permisos de ejecución.

La función mkdir falla si intentamos crear un directorio en un path que no existe

>>> os.mkdir("path/que/no/existe/directorio2")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
FileNotFoundError: [WinError 3] El sistema no puede encontrar la ruta especificada: 'path/que/no/existe/directorio2'

La función makedirs() no fallaría, crea todos los directorios que hagan falta.

>>> os.makedirs("path/que/no/existe/directorio2")
>>> os.listdir()
['directorio1', 'fichero1.txt', 'fichero2.txt', 'path']
>>> os.listdir("path")
['que']
>>> os.listdir("path/que")
['no']
>>> os.listdir("path/que/no")
['existe']
>>> os.listdir("path/que/no/existe")
['directorio2']

rmdir() y removedirs() borran directorios. El primero borra el directorio que se le indica siempre que esté vacío. Da error si no lo está.

>>> os.rmdir("directorio2")
>>> os.listdir()
['directorio1', 'fichero1.txt', 'fichero2.txt', "path"]

removedirs() al igual que makedirs() recorre todos los directorios del path que le pasamos. En esta caso, va borrándolos, siempre que estén vacíos

>>> os.removedirs("path/que/no/existe/directorio2")
>>> os.listdir()
['directorio1', 'fichero1.txt', 'fichero2.txt']

Enlaces[editar]