29. Взаимодействие с операционной системой: sys и os
Программы Python не существуют в изоляции — они запускаются в операционной системе, которая управляет файлами, каталогами, настройками окружения, а также тем, как программы запускаются и взаимодействуют. Модули sys и os предоставляют инструменты для взаимодействия с этой средой, позволяя вашим программам принимать аргументы командной строки, читать переменные окружения, перемещаться по файловой системе и создавать или удалять каталоги.
Понимание этих модулей превращает ваши скрипты Python из изолированного кода в программы, которые могут интегрироваться с более широкой системой, реагировать на ввод пользователя при запуске и программно управлять файлами и папками.
29.1) Аргументы командной строки и использование sys.argv
Когда вы запускаете скрипт Python из терминала или командной строки, вы можете передать ему дополнительную информацию — она называется аргументами командной строки (command-line arguments). Например:
python greet.py Alice 25Здесь greet.py — имя скрипта, а Alice и 25 — аргументы, переданные программе. Модуль sys предоставляет доступ к этим аргументам через sys.argv, список(list), содержащий имя скрипта и все аргументы в виде строк.
29.1.1) Что такое sys.argv?
sys.argv — это список(list), в котором:
sys.argv[0]— всегда имя запускаемого скриптаsys.argv[1],sys.argv[2]и т. д. — аргументы, переданные после имени скрипта- Все элементы — строки, даже если они выглядят как числа
Создадим простой скрипт, чтобы посмотреть, как это работает:
# show_args.py
import sys
print("Script name:", sys.argv[0])
print("Number of arguments:", len(sys.argv))
print("All arguments:", sys.argv)Если вы запустите этот скрипт так:
python show_args.py hello world 123Output:
Script name: show_args.py
Number of arguments: 4
All arguments: ['show_args.py', 'hello', 'world', '123']Обратите внимание, что sys.argv содержит 4 элемента: имя скрипта плюс три аргумента. Число 123 хранится как строка '123', а не как целое число.
29.1.2) Использование аргументов командной строки в программах
Аргументы командной строки позволяют пользователям настраивать поведение программы без изменения кода. Вот программа приветствия, которая использует первый аргумент как имя:
# greet.py
import sys
if len(sys.argv) < 2:
print("Usage: python greet.py <name>")
sys.exit(1) # Завершить работу с кодом ошибки
name = sys.argv[1]
print(f"Hello, {name}!")Запуск:
python greet.py AliceOutput:
Hello, Alice!Если вы забудете указать имя:
python greet.pyOutput:
Usage: python greet.py <name>Программа проверяет, было ли передано достаточно аргументов. Если нет, она выводит сообщение об использовании и завершает работу с sys.exit(1). Число 1 — это код завершения (exit code) — по соглашению 0 означает успех, а ненулевые значения указывают на ошибки. Это помогает другим программам или скриптам определить, успешно ли выполнилась ваша программа.
29.1.3) Преобразование аргументов в другие типы
Поскольку все аргументы приходят как строки, часто нужно выполнять преобразование. Вот программа, которая вычисляет площадь прямоугольника:
# area.py
import sys
if len(sys.argv) < 3:
print("Usage: python area.py <width> <height>")
sys.exit(1)
try:
width = float(sys.argv[1])
height = float(sys.argv[2])
except ValueError:
print("Error: Width and height must be numbers")
sys.exit(1)
area = width * height
print(f"Area: {area}")Запуск:
python area.py 5.5 3.2Output:
Area: 17.6Если пользователь введёт некорректные данные:
python area.py five threeOutput:
Error: Width and height must be numbersБлок try-except (из Главы 25) корректно обрабатывает ошибки преобразования, выдавая полезную обратную связь вместо аварийного завершения с traceback.
29.2) Получение информации об интерпретаторе и окружении выполнения с sys
Модуль sys предоставляет информацию о самом интерпретаторе Python и среде выполнения. Это полезно для отладки(debugging), логирования или написания кода, который адаптируется к разным версиям Python или платформам.
29.2.1) Информация о версии Python
sys.version и sys.version_info сообщают, какая версия Python выполняет ваш код:
import sys
print("Python version string:", sys.version)
print("Version info:", sys.version_info)
print(f"Major version: {sys.version_info.major}")
print(f"Minor version: {sys.version_info.minor}")Output (example):
Python version string: 3.11.4 (main, Jul 5 2023, 13:45:01) [GCC 11.2.0]
Version info: sys.version_info(major=3, minor=11, micro=4, releaselevel='final', serial=0)
Major version: 3
Minor version: 11sys.version — человекочитаемая строка, а sys.version_info — именованный кортеж, который можно сравнивать программно:
import sys
if sys.version_info < (3, 8):
print("This program requires Python 3.8 or higher")
sys.exit(1)
print("Python version is compatible")Это гарантирует, что ваша программа запускается только на поддерживаемых версиях Python.
29.2.2) Информация о платформе
sys.platform идентифицирует операционную систему:
import sys
print("Platform:", sys.platform)
if sys.platform == "win32":
print("Running on Windows")
elif sys.platform == "darwin":
print("Running on macOS")
elif sys.platform.startswith("linux"):
print("Running on Linux")
else:
print("Running on another platform")Output (on Linux):
Platform: linux
Running on LinuxЭто позволяет при необходимости писать платформо-зависимый код, например использовать разные пути к файлам или системные команды.
29.2.3) Путь поиска модулей
sys.path — это список(list) каталогов, в которых Python ищет модули при импорте:
import sys
print("Module search paths:")
for path in sys.path:
print(f" {path}")Output (example):
Module search paths:
/home/user/projects
/usr/lib/python3.11
/usr/lib/python3.11/lib-dynload
/home/user/.local/lib/python3.11/site-packagesПервая запись обычно — это каталог, содержащий ваш скрипт. Python ищет по этим путям по порядку, когда вы используете import. Понимание sys.path помогает отлаживать ошибки импорта или добавлять пользовательские каталоги модулей.
29.2.4) Завершение программ с кодами выхода
Мы уже видели sys.exit(), используемый для остановки программ. Вы можете передать код завершения, чтобы указать на успех или неудачу:
import sys
def process_data(filename):
try:
with open(filename) as f:
data = f.read()
# Обработать данные...
return True
except FileNotFoundError:
print(f"Error: File '{filename}' not found", file=sys.stderr)
return False
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python script.py <filename>", file=sys.stderr)
sys.exit(1)
success = process_data(sys.argv[1])
sys.exit(0 if success else 1)Коды завершения следуют соглашениям Unix:
0означает успех- ненулевые значения указывают на разные типы ошибок
- другие программы могут проверять эти коды, чтобы определить, выполнилась ли ваша программа успешно
29.3) Доступ к переменным окружения с os
Переменные окружения (environment variables) — это пары ключ-значение, задаваемые операционной системой или пользователем, которые могут читать программы. Они используются для конфигурации, хранения путей, API-ключей и других настроек, которые не следует жёстко прописывать в коде.
29.3.1) Чтение переменных окружения
Модуль os предоставляет os.environ, объект, похожий на словарь(dict), содержащий все переменные окружения:
import os
# Получить конкретную переменную окружения
home = os.environ.get('HOME') # На Unix/Linux/macOS
print(f"Home directory: {home}")
# Возвращает None, если переменная не существует
api_key = os.environ.get('MY_API_KEY')
print(f"API key: {api_key}")Output (on Linux):
Home directory: /home/alice
API key: NoneМожно указать значение по умолчанию:
import os
# Получить переменную окружения со значением по умолчанию
log_level = os.environ.get('LOG_LEVEL', 'INFO')
print(f"Log level: {log_level}")Output (if LOG_LEVEL not set):
Log level: INFO29.3.2) Распространённые переменные окружения
Разные операционные системы предоставляют стандартные переменные окружения:
import os
# Кроссплатформенные переменные
print("Path:", os.environ.get('PATH'))
# Unix/Linux/macOS
print("User:", os.environ.get('USER'))
print("Home:", os.environ.get('HOME'))
print("Shell:", os.environ.get('SHELL'))
# Windows
print("User", os.environ.get('USERNAME'))
print("User Profile:", os.environ.get('USERPROFILE'))
print("Temp:", os.environ.get('TEMP'))PATH особенно важна — она содержит список каталогов, где система ищет исполняемые программы. Когда вы вводите команду вроде python, система выполняет поиск в этих каталогах.
29.3.3) Установка переменных окружения
Вы можете изменять переменные окружения для своей программы и любых подпроцессов(subprocess), которые она создаёт:
import os
# Установить переменную окружения
os.environ['MY_CONFIG'] = 'production'
# Прочитать её обратно
print(os.environ.get('MY_CONFIG')) # Output: production
# Удалить переменную окружения
del os.environ['MY_CONFIG']Важно: изменения в os.environ влияют только на текущий процесс Python и любые программы, которые он запускает. Они не сохраняются после завершения вашей программы и не влияют на другие программы.
29.4) Работа с путями к файлам и каталогами (os.path, os.getcwd)
Корректное управление путями к файлам критически важно для программ, работающих с файлами. Модули os и os.path предоставляют инструменты для построения, изменения и проверки путей платформо-независимым способом.
29.4.1) Получение текущего рабочего каталога
Текущий рабочий каталог (current working directory, CWD) — это папка, которую программа считает отправной точкой для относительных путей:
import os
cwd = os.getcwd()
print(f"Current working directory: {cwd}")Output (example):
Current working directory: /home/alice/projects/myappКогда вы открываете файл по относительному пути вроде 'data.txt', Python ищет его в текущем рабочем каталоге. Понимание CWD помогает отлаживать ошибки «файл не найден».
29.4.2) Изменение текущего каталога
Вы можете изменить рабочий каталог с помощью os.chdir():
import os
original = os.getcwd()
print("Original directory:", original)
# Перейти в другой каталог
os.chdir('/tmp')
print("New directory:", os.getcwd())
# Вернуться обратно
os.chdir(original)
print("Back to:", os.getcwd())Output:
Original directory: /home/alice/projects
New directory: /tmp
Back to: /home/alice/projectsПримечание: В Главе 28 вы узнали про contextlib.chdir(), который автоматически восстанавливает исходный каталог. Для простого изменения каталога предпочтительнее использовать контекстный менеджер(context manager):
from contextlib import chdir
with chdir('/tmp'):
print("Temporarily in:", os.getcwd())
# Автоматически восстановленоЭто гарантирует, что каталог всегда будет восстановлен, даже если возникнет ошибка.
29.4.3) Построение путей с os.path.join()
Разные операционные системы используют разные разделители пути:
- Unix/Linux/macOS:
/(прямой слеш) - Windows:
\(обратный слеш)
os.path.join() корректно строит пути для текущей платформы:
import os
# Построить путь к файлу в подкаталоге
data_dir = 'data'
filename = 'users.txt'
filepath = os.path.join(data_dir, filename)
print(f"File path: {filepath}")Output (on Unix/Linux/macOS):
File path: data/users.txtOutput (on Windows):
File path: data\users.txtМожно объединять несколько компонентов:
import os
base = '/home/alice'
project = 'myapp'
subdir = 'data'
file = 'config.json'
full_path = os.path.join(base, project, subdir, file)
print(full_path) # Output: /home/alice/myapp/data/config.jsonИспользование os.path.join() делает ваш код переносимым между операционными системами.
29.4.4) Проверка существования путей
Перед работой с файлами или каталогами проверьте, существуют ли они:
import os
path = 'data.txt'
if os.path.exists(path):
print(f"'{path}' exists")
else:
print(f"'{path}' does not exist")Также можно проверить конкретно файлы или каталоги:
import os
path = 'mydir'
if os.path.isfile(path):
print(f"'{path}' is a file")
elif os.path.isdir(path):
print(f"'{path}' is a directory")
elif os.path.exists(path):
print(f"'{path}' exists but is neither a file nor directory")
else:
print(f"'{path}' does not exist")Эти проверки предотвращают ошибки при попытке открыть несуществующие файлы или вывести список содержимого несуществующих каталогов.
29.4.5) Получение абсолютных путей
os.path.abspath() преобразует относительные пути в абсолютные:
import os
relative_path = 'data/users.txt'
absolute_path = os.path.abspath(relative_path)
print(f"Relative: {relative_path}")
print(f"Absolute: {absolute_path}")Output (example):
Relative: data/users.txt
Absolute: /home/alice/projects/myapp/data/users.txtЭто полезно для логирования, сообщений об ошибках или когда нужно знать точное расположение файла.
29.4.6) Разделение пути на компоненты
os.path.split() разделяет путь на каталог и имя файла:
import os
path = '/home/alice/projects/data.txt'
directory, filename = os.path.split(path)
print(f"Directory: {directory}")
print(f"Filename: {filename}")Output:
Directory: /home/alice/projects
Filename: data.txtos.path.basename() получает только имя файла, а os.path.dirname() — только каталог:
import os
path = '/home/alice/projects/data.txt'
print(f"Basename: {os.path.basename(path)}") # Output: data.txt
print(f"Dirname: {os.path.dirname(path)}") # Output: /home/alice/projects29.4.7) Разделение расширений файлов
os.path.splitext() отделяет имя файла от расширения:
import os
filename = 'report.pdf'
name, extension = os.path.splitext(filename)
print(f"Name: {name}") # Output: report
print(f"Extension: {extension}") # Output: .pdfЭто полезно для обработки файлов в зависимости от их типа:
import os
files = ['data.csv', 'image.png', 'document.txt', 'script.py']
for file in files:
name, ext = os.path.splitext(file)
if ext == '.csv':
print(f"Process CSV file: {file}")
elif ext == '.png':
print(f"Process image file: {file}")Output:
Process CSV file: data.csv
Process image file: image.png29.5) Просмотр, создание и удаление файлов и каталогов
Модуль os предоставляет функции для работы с файловой системой: просмотр содержимого каталога, создание новых каталогов и удаление файлов и папок.
29.5.1) Просмотр содержимого каталога
os.listdir() возвращает список(list) всех элементов в каталоге:
import os
# Вывести содержимое текущего каталога
contents = os.listdir('.')
print("Current directory contents:")
for item in contents:
print(f" {item}")Output (example):
Current directory contents:
script.py
data.txt
mydir
README.mdСписок включает как файлы, так и каталоги. Чтобы различать их:
import os
contents = os.listdir('.')
print("Files:")
for item in contents:
if os.path.isfile(item):
print(f" {item}")
print("\nDirectories:")
for item in contents:
if os.path.isdir(item):
print(f" {item}")Output:
Files:
script.py
data.txt
README.md
Directories:
mydir29.5.2) Создание каталогов
os.mkdir() создаёт один каталог:
import os
new_dir = 'output'
if not os.path.exists(new_dir):
os.mkdir(new_dir)
print(f"Created directory: {new_dir}")
else:
print(f"Directory already exists: {new_dir}")Output:
Created directory: outputВажно: os.mkdir() завершится ошибкой, если родительского каталога не существует. Например, попытка создать 'data/output', когда 'data' не существует, вызовет ошибку.
os.makedirs() создаёт все необходимые родительские каталоги:
import os
nested_dir = 'data/processed/2024'
if not os.path.exists(nested_dir):
os.makedirs(nested_dir)
print(f"Created directory structure: {nested_dir}")Output:
Created directory structure: data/processed/2024Это создаст data, затем data/processed, затем data/processed/2024, если они не существуют.
29.5.3) Удаление файлов
os.remove() удаляет файл:
import os
filename = 'temp.txt'
# Создать временный файл
with open(filename, 'w') as f:
f.write('Temporary data')
print(f"File exists: {os.path.exists(filename)}") # Output: True
# Удалить файл
os.remove(filename)
print(f"File exists: {os.path.exists(filename)}") # Output: FalseПредупреждение: os.remove() удаляет файлы навсегда — они не попадают в корзину.
29.5.4) Удаление каталогов
os.rmdir() удаляет пустой каталог:
import os
directory = 'empty_dir'
# Создать и затем удалить пустой каталог
os.mkdir(directory)
print(f"Created: {directory}")
os.rmdir(directory)
print(f"Removed: {directory}")os.rmdir() завершится ошибкой, если в каталоге есть файлы. Чтобы удалить каталог и всё его содержимое, нужно сначала удалить файлы:
import os
def remove_directory_contents(directory):
"""Удалить все файлы в каталоге, затем удалить каталог.
Примечание: Не сработает, если каталог содержит подкаталоги.
"""
if not os.path.exists(directory):
print(f"Directory does not exist: {directory}")
return
# Удалить все файлы в каталоге
for item in os.listdir(directory):
item_path = os.path.join(directory, item)
if os.path.isfile(item_path):
os.remove(item_path)
print(f"Removed file: {item_path}")
# Удалить теперь уже пустой каталог
os.rmdir(directory)
print(f"Removed directory: {directory}")
# Пример использования
test_dir = 'test_data'
os.makedirs(test_dir, exist_ok=True)
# Создать несколько тестовых файлов
with open(os.path.join(test_dir, 'file1.txt'), 'w') as f:
f.write('test')
with open(os.path.join(test_dir, 'file2.txt'), 'w') as f:
f.write('test')
# Удалить всё
remove_directory_contents(test_dir)Output:
Removed file: test_data/file1.txt
Removed file: test_data/file2.txt
Removed directory: test_dataПримечание: Для более сложного удаления каталогов (включая подкаталоги) модуль shutil предоставляет shutil.rmtree(), но это выходит за рамки нашей текущей темы.
29.5.5) Переименование файлов и каталогов
os.rename() переименовывает или перемещает файлы и каталоги:
import os
# Переименовать файл
old_name = 'draft.txt'
new_name = 'final.txt'
# Создать тестовый файл
with open(old_name, 'w') as f:
f.write('content')
os.rename(old_name, new_name)
print(f"Renamed '{old_name}' to '{new_name}'")Также можно перемещать файлы в другие каталоги:
import os
# Создать каталоги и файл
os.makedirs('source', exist_ok=True)
os.makedirs('destination', exist_ok=True)
with open('source/file.txt', 'w') as f:
f.write('content')
# Переместить файл в другой каталог
os.rename('source/file.txt', 'destination/file.txt')
print("Moved file to destination directory")Модули sys и os дают вашим программам Python возможность взаимодействовать с операционной системой, принимать ввод из командной строки, читать конфигурацию и управлять файлами и каталогами. Эти возможности превращают простые скрипты в мощные инструменты командной строки, которые бесшовно интегрируются с более широкой средой системы.