Модули os и sys в Python
Освойте модули os и sys в Python: навигация по файловой системе, управление переменными окружения, проверка состояния интерпретатора и обработка аргументов командной строки.
Модули os и sys — два из наиболее важных инструментов стандартной библиотеки Python. os связывает Python с операционной системой — позволяет перемещаться по директориям, просматривать и задавать переменные окружения, а также управлять файлами и путями. sys связывает Python с самим интерпретатором — предоставляет список аргументов, путь поиска модулей и хуки для управления завершением работы интерпретатора. Вместе они охватывают почти всё, что нужно скрипту для взаимодействия со средой выполнения.
Модуль os
Импортируйте os в начале скрипта:
import osУстановка не требуется — модуль поставляется с каждым дистрибутивом Python.
Работа с директориями
Получение и изменение текущей рабочей директории
os.getcwd() возвращает абсолютный путь к директории, в которой выполняется скрипт. os.chdir() изменяет её.
import os
# Print the current directory
print(os.getcwd())
# Example output: /Users/alice/projects
# Change to a different directory
os.chdir("/tmp")
print(os.getcwd())
# Output: /tmpСписок содержимого директории
os.listdir(path) возвращает список всех элементов (файлов и поддиректорий) в path. Рекурсивный обход поддиректорий не выполняется.
import os
entries = os.listdir(".") # "." means current directory
for entry in sorted(entries):
print(entry)Для различия файлов и директорий используйте os.path.isfile() и os.path.isdir():
import os
for entry in os.listdir("."):
if os.path.isdir(entry):
print(f"[DIR] {entry}")
else:
print(f"[FILE] {entry}")Создание и удаление директорий
import os
# Create a single directory
os.mkdir("reports")
# Create nested directories in one call
os.makedirs("data/2024/january", exist_ok=True)
# exist_ok=True prevents an error if the directory already exists
# Remove an empty directory
os.rmdir("reports")
# Remove a full directory tree
import shutil
shutil.rmtree("data")Параметр exist_ok=True в os.makedirs() очень полезен в скриптах, которые могут запускаться несколько раз — без него повторный запуск вызовет ошибку FileExistsError.
Обход дерева директорий
os.walk(top) генерирует тройки (dirpath, dirnames, filenames) для каждой директории в дереве, начиная с top. Это стандартный способ рекурсивного обхода структуры папок.
import os
for dirpath, dirnames, filenames in os.walk("project"):
level = dirpath.count(os.sep)
indent = " " * level
print(f"{indent}{os.path.basename(dirpath)}/")
for filename in filenames:
print(f"{indent} {filename}")Работа с путями к файлам
Подмодуль os.path содержит переносимые утилиты для работы с путями, которые корректно работают в Windows, macOS и Linux.
Объединение компонентов пути
os.path.join() объединяет части пути, используя правильный разделитель для текущей ОС.
import os
base = "/home/alice"
project = "myapp"
filename = "config.json"
full_path = os.path.join(base, project, filename)
print(full_path)
# Output: /home/alice/myapp/config.jsonНикогда не строите пути через конкатенацию строк вроде base + "/" + filename — это ломается в Windows. Всегда используйте os.path.join().
Разбиение пути
import os
path = "/home/alice/myapp/config.json"
print(os.path.dirname(path)) # /home/alice/myapp
print(os.path.basename(path)) # config.json
print(os.path.split(path)) # ('/home/alice/myapp', 'config.json')
print(os.path.splitext(path)) # ('/home/alice/myapp/config', '.json')os.path.splitext() удобно использовать, когда нужно убрать или изменить расширение файла.
Проверка существования пути
import os
print(os.path.exists("/tmp")) # True (usually)
print(os.path.isfile("/tmp")) # False — it is a directory
print(os.path.isdir("/tmp")) # True
print(os.path.isabs("/tmp")) # True — it is an absolute pathПолучение абсолютного пути
os.path.abspath() преобразует относительный путь в абсолютный относительно текущей рабочей директории:
import os
print(os.path.abspath("config.json"))
# Example output: /home/alice/myapp/config.jsonЭто полезно, когда нужно сохранить или записать в лог путь, который должен оставаться корректным даже при последующем изменении рабочей директории.
Переменная __file__ и пути относительно скрипта
Распространённая ловушка: скрипт, открывающий "data.csv", работает при запуске из собственной директории, но ломается при запуске из другой. Решение — строить путь относительно самого файла скрипта:
import os
# Directory that contains *this* script
HERE = os.path.dirname(os.path.abspath(__file__))
data_file = os.path.join(HERE, "data.csv")
with open(data_file, encoding="utf-8") as f:
content = f.read()Этот приём делает скрипты переносимыми вне зависимости от того, из какой директории они запускаются.
Переменные окружения
Переменные окружения хранят конфигурацию за пределами исходного кода — URL баз данных, API-ключи, флаги функций и тому подобное. Отображение os.environ предоставляет доступ на чтение и запись к переменным окружения текущего процесса.
Чтение переменной окружения
import os
# Returns the value or None if not set
home = os.environ.get("HOME")
print(home)
# Example output: /home/alice
# Raise KeyError if not set (useful to fail fast on missing config)
path = os.environ["PATH"]Предпочитайте os.environ.get(key) вместо os.environ[key], если только переменная не является строго обязательной и программа не должна завершиться без неё.
Чтение со значением по умолчанию
import os
debug = os.environ.get("DEBUG", "false")
port = int(os.environ.get("PORT", "8080"))
print(f"debug={debug}, port={port}")
# Output: debug=false, port=8080Установка и удаление переменных окружения
import os
# Set a variable — affects only the current process and its children
os.environ["MY_APP_ENV"] = "production"
# Remove a variable
os.environ.pop("MY_APP_ENV", None) # None prevents KeyError if not presentУстановка значений os.environ не сохраняется после завершения процесса. Чтобы задать постоянные переменные окружения, измените профиль оболочки (~/.bashrc, ~/.zshrc) или используйте файл .env, загружаемый библиотекой наподобие python-dotenv.
Список всех переменных окружения
import os
for key, value in sorted(os.environ.items()):
print(f"{key}={value}")Запуск команд оболочки с помощью os.system() и subprocess
os.system(command) выполняет команду оболочки и возвращает её код завершения, но не позволяет перехватить вывод. Для чего-либо сложнее одноразового вызова используйте модуль subprocess.
import os
import subprocess
# Quick way — exit code only
exit_code = os.system("echo hello")
print("exit code:", exit_code) # 0 means success
# Better way — capture output
result = subprocess.run(
["echo", "hello"],
capture_output=True,
text=True,
)
print(result.stdout.strip()) # hellosubprocess.run() мощнее и безопаснее os.system(), поскольку не задействует интерпретатор оболочки и позволяет перехватить stdout, stderr и код возврата как объекты Python.
Полезные утилиты os
| Функция | Описание |
|---|---|
os.getcwd() | Текущая рабочая директория |
os.chdir(path) | Изменить рабочую директорию |
os.listdir(path) | Список элементов директории |
os.mkdir(path) | Создать директорию |
os.makedirs(path, exist_ok=True) | Создать вложенные директории |
os.rmdir(path) | Удалить пустую директорию |
os.remove(path) | Удалить файл |
os.rename(src, dst) | Переименовать / переместить файл |
os.walk(top) | Рекурсивный обход дерева директорий |
os.environ | Отображение переменных окружения |
os.getpid() | Идентификатор текущего процесса |
os.cpu_count() | Количество логических ядер CPU |
Модуль sys
sys предоставляет информацию об интерпретаторе Python и хуки для управления его поведением.
import sysАргументы командной строки через sys.argv
sys.argv — это список строк. sys.argv[0] содержит имя скрипта; последующие элементы — аргументы, переданные в командной строке.
Предположим, вы сохранили скрипт как greet.py и запустили python greet.py Alice 42:
import sys
script_name = sys.argv[0] # 'greet.py'
name = sys.argv[1] # 'Alice'
age = sys.argv[2] # '42' — always a string
print(f"Hello, {name}! You are {age} years old.")
# Output: Hello, Alice! You are 42 years old.Всегда проверяйте sys.argv перед обращением к индексам — IndexError завершит скрипт с ошибкой, если пользователь забудет передать аргумент:
import sys
if len(sys.argv) != 3:
print(f"Usage: python {sys.argv[0]} <name> <age>")
sys.exit(1)
name = sys.argv[1]
age = sys.argv[2]
print(f"Hello, {name}! You are {age} years old.")Для сложного разбора аргументов предпочтительно использовать модуль argparse из стандартной библиотеки — он автоматически генерирует вывод --help.
Завершение интерпретатора с помощью sys.exit()
sys.exit(code) вызывает SystemExit и завершает работу интерпретатора. По соглашению, код завершения 0 означает успех; любое ненулевое значение сигнализирует об ошибке.
import sys
answer = input("Continue? (y/n): ")
if answer.lower() != "y":
print("Goodbye!")
sys.exit(0)
print("Continuing...")Вы можете перехватить SystemExit в блоке try/except, если нужно выполнить очистку перед завершением программы, но обычно оператор with (для файловых дескрипторов, сетевых соединений и т. д.) обрабатывает очистку автоматически.
Информация о версии Python
import sys
print(sys.version)
# Example: 3.10.15 (main, ...) [GCC 11.4.0]
print(sys.version_info)
# sys.version_info(major=3, minor=10, micro=15, ...)
# Guard against running on an unsupported Python version
if sys.version_info < (3, 8):
sys.exit("This script requires Python 3.8 or later.")sys.version_info — именованный кортеж, поэтому его можно напрямую сравнивать с кортежем целых чисел.
Путь поиска модулей (sys.path)
Когда вы пишете import mymodule, Python последовательно просматривает каждую директорию в sys.path, пока не найдёт подходящий файл. Список начинается с директории скрипта (или пустой строки для интерактивных сессий), затем следуют записи PYTHONPATH, стандартная библиотека и site-packages.
import sys
for p in sys.path:
print(p)Во время выполнения можно добавить записи в sys.path, чтобы импортировать модули из нестандартных мест:
import sys
import os
# Add a sibling directory to the search path
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "libs"))
import mymodule # now found in ./libs/mymodule.pyИзменение sys.path — быстрое решение для локальной разработки, но для распространяемых пакетов используйте pip и правильный pyproject.toml.
Стандартные потоки
sys.stdin, sys.stdout и sys.stderr — объекты, подобные файлам, подключённые к трём стандартным потокам. Их можно перенаправлять для перехвата или подавления вывода.
import sys
# Write to stdout (same as print, but more explicit)
sys.stdout.write("Hello, stdout\n")
# Write to stderr (errors and diagnostics)
sys.stderr.write("Warning: something looks off\n")Распространённый приём в скриптах — перенаправить sys.stdout в файл для перехвата всего вывода print():
import sys
with open("output.log", "w", encoding="utf-8") as log:
original_stdout = sys.stdout
sys.stdout = log
print("This goes to the log file.")
sys.stdout = original_stdout
print("This goes back to the terminal.")Полезные атрибуты sys
| Атрибут / Функция | Что возвращает |
|---|---|
sys.argv | Список аргументов командной строки |
sys.version | Строка с версией Python |
sys.version_info | Именованный кортеж (major, minor, micro, ...) |
sys.platform | Идентификатор платформы ("linux", "darwin", "win32") |
sys.path | Путь поиска модулей (список строк) |
sys.modules | Словарь всех импортированных модулей |
sys.stdin | Стандартный поток ввода |
sys.stdout | Стандартный поток вывода |
sys.stderr | Стандартный поток ошибок |
sys.exit(code) | Завершить интерпретатор с указанным кодом |
sys.getrecursionlimit() | Максимальная глубина рекурсии (по умолчанию 1000) |
sys.maxsize | Максимальное значение int на данной платформе |
Совместное использование os и sys на практике
Реальные скрипты часто используют оба модуля вместе. Вот небольшой, но реалистичный пример: скрипт, сканирующий директорию на наличие файлов .log и выводящий сводку.
import os
import sys
def summarize_logs(directory):
if not os.path.isdir(directory):
sys.stderr.write(f"Error: '{directory}' is not a directory.\n")
sys.exit(1)
log_files = [
f for f in os.listdir(directory)
if f.endswith(".log") and os.path.isfile(os.path.join(directory, f))
]
if not log_files:
print("No .log files found.")
return
print(f"Found {len(log_files)} log file(s) in '{directory}':")
for name in sorted(log_files):
full_path = os.path.join(directory, name)
size = os.path.getsize(full_path)
print(f" {name} ({size} bytes)")
if len(sys.argv) != 2:
print(f"Usage: python {sys.argv[0]} <directory>")
sys.exit(1)
summarize_logs(sys.argv[1])Запустите его как python summarize.py /var/log — скрипт выведет имя и размер в байтах каждого файла .log в этой директории.
os против pathlib — что выбрать?
В Python 3.4 был введён pathlib.Path — объектно-ориентированная альтернатива os.path. Оба подхода корректны; выбор в основном определяется стилевыми предпочтениями.
| Задача | Стиль os | Стиль pathlib |
|---|---|---|
| Объединить пути | os.path.join(a, b) | Path(a) / b |
| Получить имя файла | os.path.basename(p) | Path(p).name |
| Получить расширение | os.path.splitext(p)[1] | Path(p).suffix |
| Проверить существование | os.path.exists(p) | Path(p).exists() |
| Прочитать текстовый файл | open(p).read() | Path(p).read_text() |
| Список директории | os.listdir(p) | list(Path(p).iterdir()) |
pathlib даёт более читаемый код в скриптах с интенсивной работой с путями; os.path привычен и поддерживается везде, где работает Python 3. У модуля sys нет эквивалента в pathlib — он всегда подключается как import sys.
Практика
Связанные главы
- Модули Python — создание и импорт собственных модулей
- Работа с файлами в Python — чтение и запись файлов
- Python Try Except — обработка ошибок файловых и системных операций
- Переменные Python — область видимости и именование переменных
- Область видимости Python — как Python разрешает имена в различных областях видимости