Manejo de Rutas de Archivos y Directorios

Para una inmersión profunda en las operaciones prácticas del sistema de archivos, consulte nuestra publicación de blog: 10 Operaciones Esenciales del Sistema de Archivos que Todo Desarrollador Debe Conocer.

Hay dos módulos principales en Python que se ocupan de la manipulación de rutas. Uno es el módulo os.path y el otro es el módulo pathlib.

Pathlib vs Módulo OS

pathlib proporciona mucha más funcionalidad que las enumeradas anteriormente, como obtener el nombre del archivo, obtener la extensión del archivo, leer/escribir un archivo sin abrirlo manualmente, etc. Consulte la documentación oficial si tiene la intención de saber más.

Rutas de Linux y Windows

En Windows, las rutas se escriben usando barras invertidas (\) como separador entre nombres de carpetas. En sistemas operativos basados en Unix como macOS, Linux y BSDs, se utiliza la barra inclinada hacia adelante (/) como separador de ruta. Unir rutas puede ser un dolor de cabeza si su código necesita funcionar en diferentes plataformas.

Afortunadamente, el módulo pathlib de Python proporciona una forma sencilla de manejar esto.

Usando pathlib en *nix:

# pathlib.Path: manejo de rutas multiplataforma
from pathlib import Path

print(Path('usr').joinpath('bin').joinpath('spam'))  # Unir componentes de ruta
usr/bin/spam

pathlib también proporciona un atajo para joinpath usando el operador /:

# Operador Path (/): forma conveniente de unir rutas (multiplataforma)
from pathlib import Path

print(Path('usr') / 'bin' / 'spam')  # Usar el operador / en lugar de joinpath()
usr/bin/spam

Observe que el separador de ruta es diferente entre Windows y los sistemas operativos basados en Unix, por eso querrá usar pathlib en lugar de agregar cadenas para unir rutas.

Quiz

Inicia sesión para responder este quiz y rastrear tu progreso de aprendizaje

¿Cuál es la forma correcta de unir rutas usando pathlib en Python?
A. Path('usr') + 'bin' + 'spam'
B. Path('usr') / 'bin' / 'spam'
C. Path('usr').join('bin').join('spam')
D. Path('usr/bin/spam')

Unir rutas es útil si necesita crear diferentes rutas de archivo bajo el mismo directorio.

Usando pathlib en *nix:

# Path.home(): obtener el directorio de inicio del usuario, combinar con nombres de archivo
my_files = ['accounts.txt', 'details.csv', 'invite.docx']
home = Path.home()  # Obtener la ruta del directorio de inicio
for filename in my_files:
    print(home / filename)  # Combinar la ruta de inicio con cada nombre de archivo
/home/labex/project/accounts.txt
/home/labex/project/details.csv
/home/labex/project/invite.docx

Expandir el directorio de inicio del usuario

Usando os.path.expanduser() para expandir ~ al directorio de inicio del usuario:

import os.path

# Expandir ~ al directorio de inicio del usuario
print(os.path.expanduser('~'))
/home/labex/project
# Expandir ~/Documents a la ruta completa
print(os.path.expanduser('~/Documents'))
/home/labex/project/Documents
# Funciona con rutas que contienen ~
print(os.path.expanduser('~/myfile.txt'))
/home/labex/project/myfile.txt

El directorio de trabajo actual

Puede obtener el directorio de trabajo actual usando pathlib:

# Path.cwd(): obtener el directorio de trabajo actual
from pathlib import Path

print(Path.cwd())  # Devuelve el directorio de trabajo actual como objeto Path
/home/labex/project

Creación de nuevas carpetas

Usando pathlib en *nix:

from pathlib import Path
cwd = Path.cwd()
(cwd / 'delicious' / 'walnut' / 'waffles').mkdir()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.6/pathlib.py", line 1226, in mkdir
    self._accessor.mkdir(self, mode)
  File "/usr/lib/python3.6/pathlib.py", line 387, in wrapped
    return strfunc(str(pathobj), *args)
FileNotFoundError: [Errno 2] No such file or directory: '/home/labex/project/delicious/walnut/waffles'

¡Oh no, obtuvimos un error molesto! La razón es que el directorio ‘delicious’ no existe, por lo que no podemos crear los directorios ‘walnut’ y ‘waffles’ debajo de él. Para solucionar esto, haga lo siguiente:

# mkdir(parents=True): crear directorio y todos los directorios padre si es necesario
from pathlib import Path
cwd = Path.cwd()
(cwd / 'delicious' / 'walnut' / 'waffles').mkdir(parents=True)  # Crear directorios anidados

Y todo está bien :)

Rutas Absolutas vs. Relativas

Hay dos formas de especificar una ruta de archivo.

  • Una ruta absoluta, que siempre comienza con la carpeta raíz
  • Una ruta relativa, que es relativa al directorio de trabajo actual del programa

También están las carpetas punto (.) y doble punto (..). Estas no son carpetas reales, sino nombres especiales que se pueden usar en una ruta. Un solo punto (“punto”) para un nombre de carpeta es una abreviatura de “este directorio”. Dos puntos (“doble punto”) significa “la carpeta padre”.

Manejo de rutas absolutas

Para ver si una ruta es una ruta absoluta usando pathlib:

from pathlib import Path
Path('/').is_absolute()
True
Path('..').is_absolute()
False
Quiz

Inicia sesión para responder este quiz y rastrear tu progreso de aprendizaje

¿Qué devuelve Path('/').is_absolute()?
A. True
B. False
C. None
D. '/'

Puede extraer una ruta absoluta con pathlib:

from pathlib import Path
print(Path.cwd())
/home/labex/project
print(Path('..').resolve())
/home

Manejo de rutas relativas

Puede obtener una ruta relativa desde una ruta de inicio hasta otra ruta usando pathlib:

from pathlib import Path
print(Path('/etc/passwd').relative_to('/'))
etc/passwd

Validez de la Ruta y el Archivo

Comprobar si un archivo/directorio existe

Usando pathlib en *nix:

from pathlib import Path

Path('.').exists()
True
Path('setup.py').exists()
True
Path('/etc').exists()
True
Path('nonexistentfile').exists()
False

Comprobar si una ruta es un archivo

Usando pathlib en *nix:

from pathlib import Path

Path('setup.py').is_file()
True
Path('/home').is_file()
False
Path('nonexistentfile').is_file()
False
Quiz

Inicia sesión para responder este quiz y rastrear tu progreso de aprendizaje

¿Qué devolverá Path('setup.py').is_file() si existe setup.py?
A. 'setup.py'
B. False
C. True
D. None

Comprobar si una ruta es un directorio

Usando pathlib en *nix:

from pathlib import Path

Path('/').is_dir()
True
Path('setup.py').is_dir()
False
Path('/spam').is_dir()
False

Obtener el tamaño de un archivo en bytes

Usando pathlib en *nix:

from pathlib import Path

stat = Path('/bin/python3.6').stat()
print(stat) # stat contiene otra información sobre el archivo también
os.stat_result(st_mode=33261, st_ino=141087, st_dev=2051, st_nlink=2, st_uid=0,
--snip--
st_gid=0, st_size=10024, st_atime=1517725562, st_mtime=1515119809, st_ctime=1517261276)
print(stat.st_size) # tamaño en bytes
10024

Listar directorios

Listar el contenido de un directorio usando pathlib en *nix:

from pathlib import Path

for f in Path('/usr/bin').iterdir():
    print(f)
...
/usr/bin/tiff2rgba
/usr/bin/iconv
/usr/bin/ldd
/usr/bin/cache_restore
/usr/bin/udiskie
/usr/bin/unix2dos
/usr/bin/t1reencode
/usr/bin/epstopdf
/usr/bin/idle3
...

Tamaños de archivos de directorio

ADVERTENCIA

¡Los directorios también tienen un tamaño! Por lo tanto, es posible que desee verificar si una ruta es un archivo o un directorio utilizando los métodos discutidos en la sección anterior.

Usando pathlib en *nix:

from pathlib import Path

total_size = 0
for sub_path in Path('/usr/bin').iterdir():
    total_size += sub_path.stat().st_size

print(total_size)
1903178911

Copiar archivos y carpetas

El módulo shutil proporciona funciones para copiar archivos, así como carpetas enteras.

import shutil

shutil.copy('/tmp/spam.txt', '/tmp/delicious')
/tmp/delicious/spam.txt
shutil.copy('/tmp/eggs.txt', '/tmp/delicious/eggs2.txt')
/tmp/delicious/eggs2.txt
Quiz

Inicia sesión para responder este quiz y rastrear tu progreso de aprendizaje

¿Qué función debe usar para copiar un árbol de directorios completo, incluidos todos los subdirectorios y archivos?
A. shutil.copy()
B. Path.copy()
C. os.copy()
D. shutil.copytree()

Mientras que shutil.copy() copiará un solo archivo, shutil.copytree() copiará una carpeta completa y cada carpeta y archivo que contenga:

import shutil

shutil.copytree('/tmp/bacon', '/tmp/bacon_backup')
/tmp/bacon_backup

Mover y Renombrar

import shutil

shutil.move('/tmp/bacon.txt', '/tmp/eggs')
/tmp/eggs/bacon.txt

La ruta de destino también puede especificar un nombre de archivo. En el siguiente ejemplo, el archivo de origen se mueve y se renombra:

shutil.move('/tmp/bacon.txt', '/tmp/eggs/new_bacon.txt')
/tmp/eggs/new_bacon.txt

Si no existe la carpeta eggs, entonces move() renombrará bacon.txt a un archivo llamado eggs:

shutil.move('/tmp/bacon.txt', '/tmp/eggs')
/tmp/eggs

Eliminar archivos y carpetas

  • Llamar a Path.unlink() eliminará el archivo en la ruta.
  • Llamar a Path.rmdir() eliminará la carpeta en la ruta. Esta carpeta debe estar vacía de cualquier archivo o carpeta.
  • Llamar a shutil.rmtree(path) eliminará la carpeta en la ruta, y todos los archivos y carpetas que contenga también se eliminarán.
Quiz

Inicia sesión para responder este quiz y rastrear tu progreso de aprendizaje

¿Qué método puede eliminar un directorio no vacío y todo su contenido?
A. Path.rmdir()
B. shutil.rmtree()
C. Path.unlink()
D. os.remove()

Recorrer un Árbol de Directorios

El objeto Path tiene un método rglob() para iterar recursivamente sobre archivos y directorios.

from pathlib import Path

p = Path('/tmp/delicious')
for i in p.rglob('*'):
    print(i)
/tmp/delicious/cats
/tmp/delicious/walnut
/tmp/delicious/spam.txt
/tmp/delicious/cats/catnames.txt
/tmp/delicious/cats/zophie.jpg
/tmp/delicious/walnut/waffles
/tmp/delicious/walnut/waffles/butter.txt

Enlaces relevantes