Путь к файлу — это строка, которая указывает, где в файловой системе лежит файл или каталог: цепочка имен каталогов, разделенных специальным символом, и в конце имя объекта. Абсолютный путь начинается от корня файловой системы (или от диска в Windows) и не зависит от того, где вы находитесь. Относительный путь отсчитывается от текущего рабочего каталога и без него не имеет однозначного смысла.
Содержание
Ниже разберем, как устроены пути в Windows и в Unix-подобных системах (Linux, macOS), что означают . и .., как собирать пути в Python через pathlib и как не пропустить чужой путь за пределы нужного каталога.
Мини-словарь
| Термин | Что это | Пример |
|---|---|---|
| Корень | Начальная точка дерева каталогов | / в Linux и macOS, C:\ в Windows |
| Разделитель | Символ между именами в пути | / в Unix, \ в Windows (/ Windows тоже обычно принимает) |
| Текущий рабочий каталог | Каталог, от которого считаются относительные пути у процесса | вывод pwd в терминале |
. |
Текущий каталог | ./run.sh |
.. |
Родительский каталог, на уровень выше | ../images/logo.png |
Важная поправка к популярной ошибке: переход на уровень вверх — это две точки .., а не многоточие. Имя из трех точек ... в обычных файловых системах ничего специального не означает.
Абсолютный и относительный путь
Возьмем одну структуру каталогов на диске D в Windows:
D:\Data
├── Maps
│ └── Posters
│ └── city.png
└── Shapefiles
├── Landuse
└── Soils
Абсолютный путь к картинке — D:\Data\Maps\Posters\city.png: диск, затем все каталоги по порядку, затем имя файла. Он одинаково работает из любого места.
Если текущий каталог — D:\Data\Shapefiles\Soils, то в соседний Landuse ведет относительный путь ..\Landuse: .. поднимает в Shapefiles, дальше спускаемся в Landuse. Итог — D:\Data\Shapefiles\Landuse. Тот же путь ..\Landuse, запущенный из D:\Data\Maps, приведет уже в несуществующий D:\Data\Landuse.
| Признак | Абсолютный путь | Относительный путь |
|---|---|---|
| Откуда отсчет | От корня или диска | От текущего рабочего каталога |
| Зависит от места запуска | Нет | Да |
| Переносимость проекта на другую машину | Плохая: жестко зашит чужой каталог | Хорошая, если считать от известной точки |
| Где уместен | Конфиги сервисов, cron, системные команды | Ссылки внутри проекта, HTML, импорты ресурсов |
Правило выбора: внутри проекта используйте относительные пути, но считайте их от известной точки (корня проекта или файла скрипта), а не от случайного текущего каталога. Абсолютный путь нужен там, где текущий каталог заранее неизвестен: в планировщиках задач и службах.
Windows и Unix: в чем реальная разница
| Windows | Linux, macOS | |
|---|---|---|
| Корень | Буква диска: C:\, D:\ |
Один корень /, диски монтируются в каталоги |
| Разделитель | \ (обычно работает и /) |
/ |
| Регистр имен | Как правило не различается (File.txt = file.txt) |
В Linux различается; в macOS по умолчанию нет |
| Сетевые пути | UNC: \\server\share\file.txt |
Сетевой ресурс монтируется в каталог |
| Домашний каталог | C:\Users\anna |
/home/anna, в macOS /Users/anna |
Отличий больше, чем разделитель. В Windows есть два неочевидных вида путей. \Data\Maps — путь от корня текущего диска: диска в нем нет, поэтому это не полностью абсолютный путь. D:Maps — путь относительно текущего каталога на диске D. В macOS внешние диски подключаются в /Volumes, в Linux — обычно в /mnt или /media.
Длина пути тоже ограничена по-разному. В Windows классический лимит MAX_PATH — 260 символов; длинные пути можно включить, но только если их поддерживает и система, и конкретная программа.
Пути в коде: pathlib в Python
Склеивать пути строками ("data" + "\\" + name) — источник ошибок и непереносимого кода. В Python для этого есть модуль pathlib. Минимальный полный пример, который одинаково работает на любой ОС, потому что использует «чистые» классы путей без обращения к диску:
from pathlib import PurePosixPath, PureWindowsPath
p = PurePosixPath("/home/anna/projects/report") / "data.csv"
print(p) # /home/anna/projects/report/data.csv
print(p.parent) # /home/anna/projects/report
print(p.name, p.stem, p.suffix) # data.csv data .csv
print(p.is_absolute()) # True
w = PureWindowsPath(r"D:\Data\Maps\Posters\city.png")
print(w.drive) # D:
print(w.parts)
print(PureWindowsPath(r"\Data\Maps").is_absolute()) # False
Скрипт напечатает путь, склеенный оператором /, родительский каталог, имя, основу и расширение, затем True. Для Windows-пути — диск D:, кортеж частей ('D:\\', 'Data', 'Maps', 'Posters', 'city.png') и False: путь без буквы диска pathlib абсолютным не считает.
В реальной программе берут просто Path: он сам выбирает правила текущей ОС и умеет работать с диском (exists(), read_text(), mkdir()).
Как вычисляется .., тоже стоит различать. os.path.normpath схлопывает .. чисто по тексту, а Path.resolve() смотрит на реальную файловую систему и раскрывает символические ссылки, поэтому результаты могут разойтись:
import posixpath
print(posixpath.normpath("/data/shapefiles/soils/../landuse")) # /data/shapefiles/landuse
print(posixpath.normpath("./reports/./2026/../q3.txt")) # reports/q3.txt
Частая ошибка: путь от текущего каталога, а не от скрипта
Относительный путь в open("config.json") считается от текущего рабочего каталога процесса, а не от папки, где лежит скрипт. Пока вы запускаете программу из папки проекта, все работает. Запуск из другого места, из IDE или из планировщика — и файл «пропадает». Воспроизведем:
import os
import tempfile
from pathlib import Path
project = Path(tempfile.mkdtemp())
(project / "config.json").write_text("{}")
os.chdir(project)
print(Path("config.json").exists()) # True
os.chdir(project.parent)
print(Path("config.json").exists()) # False
print((project / "config.json").exists()) # True
Первая проверка дает True, вторая — False, хотя файл никуда не делся: сменился только текущий каталог. Третья снова True, потому что путь построен от известной точки. Исправление в реальном проекте — считать путь от файла скрипта:
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent
config_path = BASE_DIR / "config.json"
print(config_path.is_absolute()) # True
Защита от path traversal
Если имя файла приходит от пользователя (форма загрузки, параметр URL), строка вида ../../etc/passwd уводит программу за пределы разрешенного каталога. Это уязвимость path traversal (обход каталога).
Неверная проверка — сравнение строк:
base = "/srv/app/uploads"
user_path = "/srv/app/uploads_evil/shell.php"
print(user_path.startswith(base)) # True
Результат True: соседний каталог uploads_evil начинается с тех же символов и проходит проверку. Кроме того, строка не раскрывает ... Исправление — нормализовать путь и сравнивать по частям пути (is_relative_to доступен с Python 3.9):
from pathlib import Path
BASE = Path("/srv/app/uploads").resolve()
def safe_path(user_name: str) -> Path:
candidate = (BASE / user_name).resolve()
if not candidate.is_relative_to(BASE):
raise ValueError(f"path outside base dir: {user_name!r}")
return candidate
for name in ["report.pdf", "../../../etc/passwd", "/etc/passwd", "docs/../report.pdf"]:
try:
print("ok ", safe_path(name).relative_to(BASE))
except ValueError as e:
print("deny", e)
Ожидаемый вывод:
ok report.pdf
deny path outside base dir: '../../../etc/passwd'
deny path outside base dir: '/etc/passwd'
ok report.pdf
Обратите внимание на третий случай: при склейке BASE / "/etc/passwd" абсолютный путь справа отбрасывает левую часть, и без проверки программа читала бы системный файл.
Это базовая проверка, а не полная защита. Надежнее вообще не использовать имя от пользователя как путь: сохранять файл под сгенерированным идентификатором, а исходное имя хранить в базе. Дополнительно нужны права процесса только на свой каталог, запрет символических ссылок из недоверенных источников и учет гонки между проверкой и открытием файла.
Как быстро узнать и скопировать путь
| Где | Как |
|---|---|
| Проводник Windows | Shift + правый клик по файлу -> «Копировать как путь» (в Windows 11 пункт есть и в обычном меню) |
| Командная строка Windows | Перетащить файл в окно — вставится полный путь; cd без аргументов покажет текущий каталог |
| Finder в macOS | Правый клик с зажатой Option -> «Скопировать путь до…» |
| Терминал Linux и macOS | pwd — текущий каталог, realpath file.txt — абсолютный путь к файлу |
Выводы
- Абсолютный путь начинается от корня или диска и не зависит от места запуска, относительный считается от текущего рабочего каталога.
.— текущий каталог,..— родительский; многоточие специального смысла не имеет.- Windows и Unix различаются корнем, разделителем, чувствительностью к регистру и лимитами длины.
- В коде пути собирают через
pathlib, а относительные пути считают от известной точки, например от__file__. - Путь от пользователя нормализуют и проверяют через
resolve()иis_relative_to(), а не сравнением строк.
Где применяется / связь с практикой
Освойте тему на практике
С путями сталкивается любой, кто пишет скрипты: чтение конфигов, обработка папок с данными, загрузка файлов на сервер, автоматизация рутины. Работу с файловой системой, модулями стандартной библиотеки и первыми проектами на Python системно разбирают на курсе Python Developer. Basic. Попробовать формат и задать вопросы преподавателям можно на бесплатных открытых уроках.
FAQ
Можно ли в Windows писать пути через прямой слеш?
Большинство API Windows и Python принимают C:/Data/file.txt, но часть старых утилит командной строки воспринимает / как начало ключа, поэтому в cmd надежнее \.
Почему в Python пишут r»C:\new»?
Без префикса r комбинация \n превратится в перевод строки. Сырая строка оставляет обратные слеши как есть; альтернатива — pathlib или прямые слеши.
Чем путь к файлу отличается от URL?
URL адресует ресурс в сети по схеме https://, всегда использует / и кодирует спецсимволы; локальный путь описывает место в файловой системе конкретной машины.



