Как получить пути к стандартным папкам пользователя (Рабочий стол, Загрузки и т.п.)?

Нужно получить пути к стандартным системным папкам пользователя, таким как:

  • Домашняя папка
  • Рабочий стол
  • Загрузки
  • Изображения
  • Документы и т. п.

Как это можно сделать кроссплатформенно?


Ответы (2 шт):

Автор решения: andreymal

Сразу нужно отметить, что подобные папки можно переместить за пределы домашней папки пользователя, так что наивные решения вроде os.path.expanduser('~/Desktop') будут работать некорректно — не делайте так.

В разных ОС есть свои правильные способы получения путей.

Windows

В Windows для этого есть Win32 API метод SHGetKnownFolderPath. К сожалению, в pywin32 этот метод ещё не поддерживается, так что этот метод придётся вызывать через ctypes, но это как-то неудобно.

Вместо этого можно установить pywin32 (с помощью команды pip install pywin32) и использовать более старый метод SHGetFolderPath, который здесь таки доступен:

from win32com.shell import shell, shellcon
path = shell.SHGetFolderPath(0, shellcon.CSIDL_DESKTOP, None, 0)
print(path)
# C:\Users\username\Desktop

Второй параметр — идентификатор папки, путь к которой мы хотим получить. Полный список можно почитать на MSDN (через pywin32 работают не все), здесь отмечу наиболее интересные:

  • shellcon.CSIDL_PROFILE — папка пользователя (C:\Users\username);
  • shellcon.CSIDL_DESKTOP — рабочий стол (C:\Users\username\Desktop);
  • shellcon.CSIDL_MYMUSIC — музыка (C:\Users\username\Music);
  • shellcon.CSIDL_MYPICTURES — изображения (C:\Users\username\Pictures);
  • shellcon.CSIDL_MYVIDEO — видео (C:\Users\username\Videos).

Linux, *BSD (XDG)

Unix-подобные ОС обычно опираются на стандартны freedesktop.org (больше известны по аббревиатуре XDG) и хранят пути к стандартным папкам в файле ~/.config/user-dirs.dirs — получить интересующий путь в терминале можно командой xdg-user-dir (это короткий bash-скрипт на несколько строк), например xdg-user-dir DOWNLOAD.

Каждая строка в файле user-dirs.dirs имеет переменную в формате XDG_xxx_DIR, где xxx — тип каталога. Путь к каталогу должен или быть абсолютный (начинаться на /), или начинаться на $HOME/ — другие форматы не допускаются.

К своему удивлению, я не обнаружил готового парсера этого файла для Python; впрочем, его нетрудно написать самостоятельно, используя библиотеку dotenv (pip install python-dotenv, не забудьте добавить опцию --user если нужно):

import os
from dotenv import dotenv_values

def get_user_dir(name: str) -> str:
    # Сперва получаем домашний каталог традиционным способом
    home_dir = os.path.expanduser("~")

    # Потом ищем путь к каталогу, в котором хранятся настройки
    config_dir = os.getenv("XDG_CONFIG_HOME") or os.path.join(home_dir, ".config")

    # Загружаем файл с описанием каталогов
    data = dotenv_values(os.path.join(config_dir, "user-dirs.dirs"))
    
    # Получаем путь из файла или генерируем его, если в файле нет нужного пути
    key = f"XDG_{name}_DIR"
    if key in data:
        path = data[key]
    elif key == "DESKTOP":
        path = os.path.join(home_dir, "Desktop")
    else:
        path = home_dir

    # Если путь начинается на $HOME, то нужно подставить туда домашний каталог
    if path.startswith("$HOME/"):
        path = os.path.join(home_dir, path[6:])

    return path
print(get_user_dir("DOWNLOAD"))
# /home/username/Загрузки

В этом файле представлены следующие переменные (названия папок могут отличаться в зависимости от текущего языка системы):

  • XDG_DESKTOP_DIR/home/username/Рабочий стол
  • XDG_DOWNLOAD_DIR/home/username/Загрузки
  • XDG_TEMPLATES_DIR/home/username/Шаблоны
  • XDG_PUBLICSHARE_DIR/home/username/Общедоступные
  • XDG_DOCUMENTS_DIR/home/username/Документы
  • XDG_MUSIC_DIR/home/username/Музыка
  • XDG_PICTURES_DIR/home/username/Изображения
  • XDG_VIDEOS_DIR/home/username/Видео
→ Ссылка
Автор решения: 0x7633

В линуксе ещё можно таким вариантом воспользоваться:

#!/usr/bin/python3

import subprocess

user_dirs = {}

args = ['DESKTOP', 'DOWNLOAD', 'TEMPLATES', 'PUBLICSHARE', 'DOCUMENTS', 'MUSIC', 'PICTURES', 'VIDEOS']

for arg in args:
    r = subprocess.run(['xdg-user-dir', arg], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    user_dirs[arg.lower()] = r.stdout.decode('utf-8').rstrip()

for key in user_dirs:
    print(f'{key}: {user_dirs[key]}')

Вывод:

desktop: "/home/user/Рабочий стол"
download: "/home/user/Загрузки"
templates: "/home/user/Шаблоны"
publicshare: "/home/user/Общедоступные"
documents: "/home/user/Документы"
music: "/home/user/Музыка"
pictures: "/home/user/Изображения"
videos: "/home/user/Видео"
→ Ссылка