RegSort
Скачать здесь, читать подробнее здесь.
Утилита эта для Windows, но без проблем работает и под WINE в Linux например как-то так: wine ~/.wine/drive_c/Program\ Files/RegSort.exe settings.ini. Отсортированную версию файла она создаёт рядом с исходным, так что переименовать исходный в settings.ini.bak, а settings_Sorted.ini в settings.ini придётся самостоятельно. Это неудобно, но проверенно работает.
sortini.py
Я не умею кодить, но вайбкодить мне никто не запрещал, так что ChatGPT дал мне вот такой скрипт. Он работает, но с оговорками. Сперва он создаёт .bak и только затем сортирует секции и ключи в файле. Если бэкап уже существует, он может быть затёрт, прошу иметь в виду. И как он работает с кодировками, отличными от UTF-8, я не знаю, хотя это вряд ли актуально. Если хотите исправить и дополнить, пожалуйста делайте это, только обязательно дайте мне знать, потому что сейчас этот скрипт так себе, а у меня не хватит ума довести до ума.
#!/usr/bin/env python3
#
# sortini.py — простая сортировка .ini
# Навайбкодил с помощью ChatGPT: Eoin Gairleog
# Назначение: создаёт .bak, сортирует секции по алфавиту внутри .ini, затем сортирует ключи внутри секций
# Лицензия: WTFPL
# Версия: 1.0 (the first and the last)
# Совместимость: Python 3.8+
import sys
from pathlib import Path
def process_ini(path: Path):
backup = path.with_suffix(path.suffix + ".bak")
backup.write_bytes(path.read_bytes())
out = []
# читаем вручную, без configparser (он ломает формат)
raw = path.read_text(encoding="utf-8").splitlines()
sections = {}
current = None
buf = []
def flush():
if current is not None:
sections[current] = buf.copy()
for line in raw:
stripped = line.strip()
if stripped.startswith("[") and stripped.endswith("]"):
flush()
current = stripped
buf = []
else:
if current is None:
# строки вне секций оставляем как есть
out.append(line)
else:
buf.append(line)
flush()
# сортируем секции
for sec in sorted(sections.keys(), key=str.lower):
out.append(sec)
body = sections[sec]
keys = []
rest = []
for l in body:
s = l.strip()
if "=" in s and not s.startswith(";") and not s.startswith("#"):
keys.append(l)
else:
rest.append(l)
# сортируем только пары ключ=значение
keys_sorted = sorted(keys, key=lambda x: x.split("=")[0].strip().lower())
for l in keys_sorted:
out.append(l)
for l in rest:
out.append(l)
path.write_text("\n".join(out) + "\n", encoding="utf-8")
if __name__ == "__main__":
if len(sys.argv) < 2:
print("usage: sortini.py file1.ini [file2.ini ...]")
sys.exit(1)
for fp in sys.argv[1:]:
process_ini(Path(fp))