Коллекция bash/Python-скриптов для автоматизации повседневных задач.
Скрипты предоставляются "AS IS" («как есть»), без каких-либо гарантий. Подробнее см. файл LICENSE.
Конфигурационные файлы скриптов находятся в папке conf/ в корне репозитория.
Скрипты, использующие конфигурацию, создают каталог conf/ при первом запуске,
если его ещё нет. Рабочие *.conf содержат машинно-зависимые пути и параметры,
поэтому не коммитятся; в репозитории хранятся безопасные *.conf.example.
Актуальные шаблоны:
auto_delete_old_files.conf.example;btrfs_monitor.conf.example;cloud_backup.conf.example;cloud_backup_qnap.conf.example;getip.conf.example;log_template.conf.example;sources.conf.example;split_by_dash.conf.example;system_monitor.conf.example.
Скопируйте нужный шаблон без суффикса .example и заполните локальными
значениями. all_to_mp3.sh при необходимости читает
conf/audio_to_mp3.conf; его параметры перечислены в разделе скрипта ниже.
Shell-скрипты в корне репозитория используют общие соглашения:
- Единая структура секций:
SCRIPT ID / PATHS,HELPERS,ARGS,MAIN - Явные проверки параметров и зависимостей
- Безопасные режимы запуска shell (
set -euилиset -uo pipefailв зависимости от логики скрипта) - для скриптов с JSONL-логированием — каталог
logs/и одна JSON-запись на строку; - ротация логов по параметру конкретного скрипта либо по встроенному лимиту.
Единый шаблон хранится в conf/log_template.conf.
Для коммита в репозиторий используйте пример conf/log_template.conf.example.
Shell-скрипты с JSONL-логированием читают этот файл, если он доступен, и пишут унифицированные поля, подходящие для ELK/OpenSearch, Loki, Graylog и Splunk:
@timestampschema.versioncompat.targetslog.levelmessageevent.actionservice.name
Также сохраняются служебные поля совместимости (script, event, msg, detail, rc и т.д.), чтобы не ломать существующие разборщики.
Это упрощает сопровождение, делает поведение скриптов предсказуемым и улучшает диагностику ошибок.
Пакетная конвертация аудиофайлов в MP3 с помощью ffmpeg.
Возможности:
- Поддерживает любые форматы, распознаваемые
ffprobe - Обработка файлов в несколько потоков (
xargs -P) - Встраивает обложку — внешнюю (cover.jpg, folder.jpg, front.jpg) или встроенную в аудио
- Сохраняет структуру каталогов в выходной папке
- Ведёт отчёт (
audio_to_mp3_report.txt) и лог ошибок (audio_to_mp3_errors.log)
Конфигурация (опциональный файл conf/audio_to_mp3.conf):
| Параметр | По умолчанию | Описание |
|---|---|---|
FORMATS |
(все форматы) | Список расширений для обработки |
QUALITY |
0 |
Качество MP3 (0 — наилучшее) |
COVER_NAMES |
cover.jpg folder.jpg front.jpg |
Имена файлов обложек |
JOBS |
nproc |
Количество параллельных потоков |
OUTPUT_DIR |
/copy/Music |
Папка для результата |
Использование:
./all_to_mp3.sh /путь/к/музыкеРезервное копирование /opt/esimych-cloud с удалённого сервера через WireGuard VPN.
Порядок работы:
- Поднимает WireGuard-соединение (
wg-quick up), если оно не активно - Подключается к удалённому серверу по SSH (
sshpass) - Выбирает компрессор на удалённом сервере:
zstd,pigzилиgzip - Чистит корзины всех пользователей и версии файлов Nextcloud (
occ trashbin:cleanup --all-users,occ versions:cleanup) - (Опционально) оптимизирует MariaDB (
mariadb-check --optimize, очисткаgeneral.log, опционально purge binlogs) - (Опционально) оптимизирует Redis AOF (
BGREWRITEAOF) - Создаёт архив удалённой папки и сохраняет его в
BACKUP_DIR - Если архив за текущую дату уже существует и проходит проверку целостности, дозаписывает новый поток в тот же файл
- Если существующий архив пустой или повреждённый, удаляет его и создаёт заново
- Опускает WireGuard (если был поднят скриптом) — управляется параметром
WG_KEEP_UP
Зависимости: wg-quick, sshpass (для опциональной оптимизации: redis-cli в соответствующем контейнере)
Конфигурация (conf/cloud_backup.conf):
| Параметр | По умолчанию | Описание |
|---|---|---|
WG_INTERFACE |
(обязательно) | Имя WireGuard-интерфейса (напр. wg0) |
REMOTE_HOST |
(обязательно) | IP-адрес удалённого сервера |
REMOTE_USER |
(обязательно) | Логин SSH |
REMOTE_PASSWORD |
(обязательно) | Пароль SSH |
SUDO_PASSWORD |
(пусто) | Пароль для неинтерактивного sudo wg-quick; при пустом значении wg-quick вызывается напрямую |
REMOTE_SSH_PORT |
22 |
Порт SSH |
REMOTE_PATH |
/opt/esimych-cloud |
Путь к папке на удалённой системе |
BACKUP_DIR |
(обязательно) | Локальная папка для резервных копий |
WG_KEEP_UP |
0 |
Оставить WireGuard активным после завершения |
BACKUP_KEEP_COUNT |
5 |
Сколько последних резервных копий и логов хранить |
BACKUP_DEGRADATION_MIBS_THRESHOLD |
6 |
Порог средней скорости (MiB/s) для события backup_degradation |
OPTIMIZE_MARIADB_BEFORE_BACKUP |
0 |
Выполнить оптимизацию MariaDB перед архивом |
MARIADB_SERVICE_NAME |
mariadb |
Имя MariaDB-сервиса в docker compose |
MARIADB_PURGE_BINLOGS |
0 |
Очистить старые binary logs (PURGE BINARY LOGS) |
MARIADB_TRUNCATE_GENERAL_LOG |
1 |
Очистить general.log перед архивом |
OPTIMIZE_REDIS_BEFORE_BACKUP |
0 |
Выполнить BGREWRITEAOF для Redis |
REDIS_SERVICE_NAME |
redis |
Имя Redis-сервиса в docker compose |
REDIS_REWRITE_WAIT_SEC |
180 |
Верхний предел ожидания BGREWRITEAOF (сек) — не типичная длительность: определение завершения устойчиво к CRLF в выводе redis-cli INFO, поэтому при штатной работе rewrite завершается за секунды, значение — лишь потолок на случай реальной проблемы |
Логи: JSONL-файл logs/cloud_backup-YYYY-MM-DD-HH-MM-SS.jsonl. Хранится BACKUP_KEEP_COUNT последних файлов (тот же параметр, что и для архивов).
Имя архива: cloud_backup-YYYY-MM-DD.tar.gz или cloud_backup-YYYY-MM-DD.tar.zst
Поведение при существующем архиве:
tar.gzиtar.zstпроверяются на целостность перед дозаписью- валидный архив за текущую дату дополняется новым сжатым потоком
- битый или пустой архив удаляется и пересоздаётся
- если локально недоступна утилита проверки (
gzipилиzstd), скрипт не дозаписывает, а создаёт архив заново
Встроенные исключения при архивировании (всегда включены):
tmp/*,cache/*,.cache/*в корнеREMOTE_PATHdata/*/uploads/*data/*/files_trashbin/uploads/*data/*/cache/*data/appdata_*/preview/*data/appdata_*/thumbnails/*data/appdata_*/css/*data/appdata_*/js/*
Эти excludes жёстко заданы в скрипте и не настраиваются через conf/cloud_backup.conf.
Очистка корзины Nextcloud перед архивированием:
occ trashbin:cleanup --all-usersфизически удаляет саму папкуdata/<user>/files_trashbin(не только её содержимое), если она становится пустой- из-за этого встроенный фоновый джоб Nextcloud
ExpireTrash(запускается черезcron.phpнезависимо от расписания бэкапа) затем падает сNotFoundException.../files_trashbinпри каждом своём запуске - поэтому сразу после очистки скрипт получает
datadirectoryи список пользователей черезoccи для каждого пользователя выполняетmkdir -p <datadir>/<user>/files_trashbin— это идемпотентно и безопасно
Производительность:
- SSH-шифр: приоритет
aes128-gcm,chacha20-poly1305— используют аппаратное ускорение AES-NI - SSH-сжатие отключено (
Compression=no) — tar уже сжимает данные, двойное сжатие только тратит CPU - Компрессор выбирается на удалённом сервере:
zstd --fast=1 --threads=0, затемpigz -1, при их отсутствии —gzip -1 set -o pipefailна удалённой стороне — ошибкаtarкорректно прерывает передачу- JSONL-лог очищается от управляющих символов в
detail, чтобы записи были устойчиво парсируемы - Пишутся диагностические события
backup_metrics(размер/длительность/скорость) иbackup_degradationпри скорости ниже порога - Порог деградации можно задать через
BACKUP_DEGRADATION_MIBS_THRESHOLD(MiB/s), по умолчанию6
Использование:
sudo ./cloud_backup.shCron (каждый день в 21:30):
30 21 * * * root /path/to/mytasks/cloud_backup.sh >> /dev/null 2>&1Или через sudo crontab -e:
30 21 * * * /home/esimych/esimych-docs/mytasks/cloud_backup.shQNAP-версия cloud_backup.sh — то же резервное копирование /opt/esimych-cloud через WireGuard VPN, но запускается на самом QNAP NAS (Entware cron / Task Scheduler), а не на десктопе.
Отличия от cloud_backup.sh:
- Shebang
#!/opt/bin/bash— Entware bash напрямую (штатный/bin/shна QTS не поддерживает массивы/local/${!var}) PATHдополняется/opt/sbin:/opt/bin(Entware), т.к. QTS не содержитwg-quick,wg,zstd- Нет sudo-кода — Task Scheduler/cron на QNAP и так выполняют скрипт от root
- WireGuard поднимается в обход
wg/wg-quick— на части моделей QTS (проверено на ядре 4.2.8 aarch64) консольная утилитаwg(8)не работает в принципе (Protocol not supported), поэтому туннель поднимается напрямую через userspace UAPI-сокетwireguard-go(см.wg_up_userspace/wg_conf_getв коде) - Основной способ аутентификации — SSH-ключ (
REMOTE_SSH_KEY); пароль используется только если одновременно заданыREMOTE_PASSWORDи доступенsshpass - Автоматически переподключает bind-mount
/opt→ENTWARE_DATA_DIR, если он слетел после перезагрузки NAS (ensure_entware_mount) - Как и десктопная версия, чистит корзину и версии файлов Nextcloud (
occ trashbin:cleanup --all-users,occ versions:cleanup) и пересоздаёт папкуfiles_trashbinдля каждого пользователя после очистки. Команды выполняются через имя сервиса Compose (NEXTCLOUD_SERVICE_NAME), поэтому не зависят от изменяемогоcontainer_name - При доступном
zstdудалённый поток сжимается командойzstd -5 --threads=0 -c: уровень 5 выбран как компромисс между дополнительным сжатием и нагрузкой на удалённый сервер; при отсутствииzstdсохраняется автоматический переход наpigz -1илиgzip -1
Предварительная настройка на QNAP (разово, вручную) подробно описана в шапке самого скрипта: установка Entware, пакетов (opkg install bash wireguard-tools wireguard-go ncat xxd zstd coreutils-stat), создание конфига WireGuard-клиента с отдельным peer/IP, проверка userspace UAPI-сокета, копирование SSH-ключа на сервер, заполнение conf/cloud_backup_qnap.conf и (опционально, только для RAW_TRANSFER_ENABLED="1") открытие порта передачи в фаерволе удалённого сервера — см. шаг 8 в шапке скрипта.
Конфигурация (conf/cloud_backup_qnap.conf, см. .example):
| Параметр | По умолчанию | Описание |
|---|---|---|
WG_INTERFACE |
(обязательно) | Имя WireGuard-интерфейса (напр. wg0-qnap) |
WG_ENDPOINT |
(пусто) | Необязательное переопределение публичного endpoint WireGuard в формате IPv4:port; если пусто, берётся Endpoint из /opt/etc/wireguard/<WG_INTERFACE>.conf |
ENTWARE_DATA_DIR |
/share/Public/entware |
Постоянный каталог Entware, который скрипт при необходимости подключает в /opt |
REMOTE_HOST |
(обязательно) | Внутренний VPN-адрес удалённого сервера; при смене публичного IP не меняется |
REMOTE_USER |
(обязательно) | Логин SSH |
REMOTE_PASSWORD |
(необязательно, пусто) | Пароль SSH (используется только если найден sshpass) |
REMOTE_SSH_KEY |
(рекомендуется) | Путь к приватному SSH-ключу |
REMOTE_SSH_PORT |
22 |
Порт SSH |
REMOTE_PATH |
/opt/esimych-cloud |
Путь к папке на удалённой системе |
BACKUP_DIR |
(обязательно) | Папка на QNAP для резервных копий (напр. /share/Public/backups) |
WG_KEEP_UP |
0 |
Оставить WireGuard активным после завершения |
BACKUP_KEEP_COUNT |
5 |
Сколько последних резервных копий и логов хранить |
NEXTCLOUD_SERVICE_NAME |
nextcloud |
Имя Nextcloud-сервиса в docker compose для команд occ |
SERVICES_START_RETRIES |
3 |
Число попыток запуска и проверки сервисов после бэкапа |
SERVICES_START_RETRY_DELAY_SEC |
15 |
Пауза между повторными попытками запуска сервисов |
OPTIMIZE_MARIADB_BEFORE_BACKUP |
0 |
Выполнить оптимизацию MariaDB перед архивом |
MARIADB_SERVICE_NAME |
mariadb |
Имя MariaDB-сервиса в docker compose |
MARIADB_PURGE_BINLOGS |
0 |
Очистить старые binary logs |
MARIADB_TRUNCATE_GENERAL_LOG |
1 |
Очистить general.log перед архивом |
OPTIMIZE_REDIS_BEFORE_BACKUP |
0 |
Выполнить BGREWRITEAOF для Redis |
REDIS_SERVICE_NAME |
valkey |
Имя сервиса Redis/Valkey в docker compose |
REDIS_REWRITE_WAIT_SEC |
180 |
Верхний предел ожидания BGREWRITEAOF (сек), не типичная длительность (см. пояснение в разделе cloud_backup.sh выше) |
REDIS_LOG_TAIL_LINES |
300 |
Число последних строк Redis/Valkey, сохраняемых перед docker compose down |
DB_OPTIMIZE_INTERVAL_DAYS |
7 |
Интервал выполнения включённых оптимизаций MariaDB/Redis; дата последнего запуска хранится в state/cloud_backup_qnap-db-optimize.state |
SSH_CIPHERS |
chacha20-poly1305@openssh.com,aes128-gcm@openssh.com,aes256-gcm@openssh.com,aes128-ctr |
Приоритет SSH-шифров; chacha20-poly1305 первым, т.к. на слабых ARM-чипах без аппаратного AES он обычно быстрее программного AES-GCM/CTR, а расшифровку входящего потока бэкапа выполняет именно QNAP |
RAW_TRANSFER_ENABLED |
0 |
Передавать поток бэкапа через сырой TCP (nc/ncat) внутри WireGuard-туннеля в обход дополнительного слоя SSH-шифрования — см. раздел «Передача в обход SSH-шифрования» ниже. 0 — обычная передача через SSH (без изменений) |
RAW_TRANSFER_PORT |
8873 |
TCP-порт для приёма потока на удалённом сервере (нужно один раз открыть в фаерволе, см. шаг 8 в шапке скрипта) |
RAW_TRANSFER_CONNECT_RETRIES |
10 |
Число попыток подключения к листенеру (по 2 сек между попытками) |
RAW_TRANSFER_REMOTE_TIMEOUT_SEC |
21600 |
Потолок времени жизни удалённого листенера (защита от зависшего процесса, если QNAP так и не подключился) |
RAW_TRANSFER_STATUS_POLL_RETRIES |
5 |
Число попыток получить статус завершения удалённого пайплайна после приёма данных |
NETWORK_SPEED_TEST_MB |
50 |
Размер тестовой передачи (МБ) для замера реальной скорости сети QNAP↔REMOTE_HOST перед началом бэкапа (событие network_speed_test) |
BACKUP_DEGRADATION_MIBS_THRESHOLD |
4 |
Порог средней скорости, ниже которого создаётся backup_degradation |
Логи: JSONL-файл logs/cloud_backup_qnap-YYYY-MM-DD-HH-MM-SS.jsonl на QNAP. Хранится BACKUP_KEEP_COUNT последних файлов (см. cleanup_logs, тот же параметр, что и для архивов).
Диагностика скорости: перед передачей и во время неё скрипт фиксирует
сетевой тест, RTT, load average, доступную память и IO QNAP. Итоговое событие
backup_metrics содержит размер, длительность, среднюю скорость и агрегированные
ресурсные показатели. При скорости ниже
BACKUP_DEGRADATION_MIBS_THRESHOLD создаётся backup_degradation с вероятной
причиной. Ошибки raw-transfer дополняются снимком состояния удалённого сервера.
После бэкапа docker compose up -d и состояние контейнеров проверяются до SERVICES_START_RETRIES раз. Если после всех попыток остаются unhealthy, exited, dead или restarting контейнеры, в лог записываются docker compose ps -a и логи Redis/Valkey, а сам скрипт завершается с ненулевым кодом.
Передача в обход SSH-шифрования (RAW_TRANSFER_ENABLED): при значении
1 сжатый поток передаётся через сырой TCP внутри WireGuard-туннеля, без
дополнительного SSH-шифрования. WireGuard продолжает обеспечивать
конфиденциальность и целостность; SSH используется для управляющих команд и
запуска листенера.
Для корректного завершения потока используются:
- удалённая сторона:
nc -N -l ...— без флага-NOpenBSD nc не закрывает сокет по EOF своего stdin и висит доRAW_TRANSFER_REMOTE_TIMEOUT_SEC(часы) вместо того, чтобы завершиться сразу после отправки всех данных; - сторона QNAP:
ncat --recv-only ... < /dev/null— флаг запрещает отправку локального stdin, а перенаправление позволяет процессу завершиться после получения EOF от сервера.
Порт RAW_TRANSFER_PORT нужно открыть в фаерволе удалённого сервера. При
недоступности nc или исчерпании попыток подключения скрипт автоматически
переходит на SSH-передачу.
Имя архива и поведение при дозаписи — идентично cloud_backup.sh (см. выше): cloud_backup_qnap-YYYY-MM-DD.tar.zst/.tar.gz, проверка целостности и дозапись валидного архива за текущую дату, встроенные excludes для кэшей/preview/tmp.
Планирование запуска на QNAP — Планировщик задач QTS (если доступен) либо персистентный crontab (в отличие от обычного Linux, /etc/config/crontab переживает перезагрузку NAS, т.к. хранится в конфигурации прошивки):
vi /etc/config/crontab
# добавить строку (например, каждый день в 04:00):
0 4 * * * /share/Public/tasks/cloud_backup_qnap.sh >/dev/null 2>&1
crontab /etc/config/crontab
/etc/init.d/crond.sh restartВажно: не запускайте cloud_backup.sh (десктоп) и cloud_backup_qnap.sh одновременно против одного и того же REMOTE_HOST — оба скрипта независимо управляют docker compose down/up -d на удалённом сервере, и параллельный запуск может привести к преждевременному перезапуску сервисов или конфликту жизненного цикла контейнеров.
Важно: в crontab вызывайте скрипт напрямую, не через sh: явный
sh /share/Public/tasks/cloud_backup_qnap.sh игнорирует шебанг
#!/opt/bin/bash и запускает Bash-скрипт системным /bin/sh QTS.
Автоматически удаляет старые файлы по сроку хранения из указанной папки.
Конфигурация (conf/auto_delete_old_files.conf):
| Параметр | По умолчанию | Описание |
|---|---|---|
TARGET_DIR |
(обязательно) | Каталог, где нужно удалять старые файлы |
FILE_TYPES |
all |
Типы файлов: all или список расширений (log,tmp,bak) |
KEEP_DAYS |
30 |
Сколько дней хранить файлы |
Правило удаления: удаляются только файлы (-type f) старше KEEP_DAYS дней.
Использование:
./auto_delete_old_files.shCron (каждый день в 20:30):
30 20 * * * /home/esimych/esimych-docs/mytasks/auto_delete_old_files.sh >/dev/null 2>&1Проверяет, смонтированы ли все точки монтирования из /etc/fstab.
Возможности:
- Пропускает виртуальные ФС (
/proc,/sys,/dev,swapи др.) - При обнаружении несмонтированных точек отправляет desktop-уведомление через
notify-send - Дублирует сообщение в консоль
- Предназначен для запуска через cron или autostart
Использование:
./check-store-mount.shМониторинг счётчиков ошибок Btrfs на корневом разделе (или другой точке монтирования).
Что делает:
- Читает
btrfs device statsдляROOT_PATH - Сравнивает с прошлым запуском (state-файл)
- При росте счётчиков выводит alert, пишет
ERRORв JSONL-лог и завершает работу с кодом1 - На первом запуске только инициализирует baseline
Конфигурация:
conf/btrfs_monitor.conf- пример:
conf/btrfs_monitor.conf.example
Использование:
./btrfs_monitor.shCron (каждые 6 часов):
0 */6 * * * /home/esimych/esimych-docs/mytasks/btrfs_monitor.sh >/dev/null 2>&1Диагностика системы: жёсткие диски через SMART (smartctl), CPU (температура,
загрузка, load average) и GPU (NVIDIA, через nvidia-smi). Опрос, запись
состояния в SQLite и формирование HTML-отчёта (таблицы, диаграммы,
аналитическая справка по каждому диску). Предназначен для запуска по
расписанию и/или при загрузке системы.
Что делает:
Диски:
- Определяет физические диски автоматически через
lsblk(или берёт список изDISKSв конфиге), пропускаяzram,loop,ram,sr,dm- - Опрашивает каждый диск через
smartctl -a -j(JSON) и разбирает результат черезjq: health, модель, серийный номер, температура, наработка (power-on hours),Reallocated_Sector_Ct,Current_Pending_Sector,Offline_Uncorrectable(для NVMe — соответствующие поляnvme_smart_health_information_log) - Определяет устойчивое системное имя диска (
/dev/disk/by-id/..., напримерata-ST4000DM004-2U9104_WW608PYW) — не меняется при перестановке дисков/перезагрузке, в отличие от/dev/sdX; хранится в SQLite вместе с остальными метриками - Определяет точки монтирования диска через
lsblk -J+jq, с привязкой к разделу (напримерnvme0n1p1: /, /var/log, /var/cache, /var/tmp, /root, /srv; nvme0n1p2: /boot/efi) — на btrfs один и тот же раздел часто смонтирован сразу в несколько мест через подтома (subvolumes); JSON-разбор черезjqберётся не случайно: построчныйlsblk -o MOUNTPOINT(ед. число) отдаёт только одну точку на раздел и может молча потерять корень/ - Определяет объём диска в байтах через
lsblk -bdn -o SIZE(не зависит отsmartctl— доступен даже если SMART недоступен/диск не поддерживается) и показывает его в отчёте в человекочитаемом виде (КиБ/МиБ/ГиБ/ТиБ)
CPU:
- Температура — через
lm_sensors(sensors -j), ищет главный тепловой датчик пакета (Tctl/Tdieна AMD,Package id 0на Intel); опционально — еслиsensorsне установлен/не настроен, CPU всё равно опрашивается, просто без температуры - Загрузка (%) — две выборки
/proc/statс интервалом в 1 секунду (тот же принцип, что используетtop/mpstat), без дополнительных зависимостей - Load average (1/5/15 мин) — из
/proc/loadavg
GPU (NVIDIA):
- Через
nvidia-smi --query-gpu=... --format=csv: модель, температура, загрузка (%), использование видеопамяти, потребляемая мощность — по каждой видеокарте отдельно (поддержка нескольких GPU) - Опционально — если
nvidia-smiне найден (нет NVIDIA GPU или драйвера), опрос GPU просто пропускается, это не ошибка
Отчёт и хранение:
- Пишет каждый опрос как строки в таблицы
disk_stats/cpu_stats/gpu_statsв SQLite (DB_FILE), не перезаписывая историю — на её основе строятся графики. Все данные, которые попадают в отчёт, сначала сохраняются в SQLite (ничего не вычисляется «на лету» только для HTML) — это сделано специально, чтобы дальше можно было строить аналитические отчёты поверх накопленной истории - Формирует HTML-отчёт целиком из данных SQLite: сводные плитки (кол-во объектов в норме/внимание/критично по дискам+CPU+GPU вместе), таблица метрик по каждому диску (с колонками «Точка монтирования» и «Объём»), аналитическая справка по каждому диску (деградация поверхности, риск отказа, перегрев, точки монтирования и т.д.), таблицы и графики по CPU и GPU, график истории температуры дисков и диаграмма Reallocated/Pending-секторов — всё встроенным SVG, без внешних JS-библиотек. Графики CPU/GPU совмещают температуру (°C) и проценты (загрузка, VRAM) на одной общей шкале 0-100 — это не «две оси», а честно общий диапазон, единица измерения проставлена у каждой точки
- При критическом состоянии любого объекта (диск: SMART FAILED/неисправимые ошибки чтения/температура выше
TEMP_CRIT_C; CPU/GPU: температура вышеCPU_TEMP_CRIT_C/GPU_TEMP_CRIT_C) отправляет desktop-уведомление черезnotify-send(какbtrfs_monitor.sh/check-store-mount.sh) и завершается с кодом1; предупреждения (переназначенные/ожидающие секторы диска, температура выше порога «внимание») только пишутся в лог, без уведомления - Ротирует и JSONL-логи (
KEEP_LOGS), и HTML-отчёты (REPORT_KEEP)
Конфигурация:
conf/system_monitor.conf- пример:
conf/system_monitor.conf.example
| Параметр | По умолчанию | Описание |
|---|---|---|
DISKS |
(пусто = автоопределение) | Список дисков через запятую (sda,sdb), пусто — автоопределение через lsblk |
DB_FILE |
state/system_monitor.db |
Путь к базе SQLite с историей опросов |
REPORT_DIR |
system_reports/ рядом со скриптом |
Каталог для HTML-отчётов |
REPORT_KEEP |
5 |
Сколько последних HTML-отчётов хранить |
KEEP_LOGS |
10 |
Сколько последних JSONL-логов хранить |
HISTORY_POINTS |
30 |
Сколько последних опросов показывать на графиках истории |
SMARTCTL_BIN |
smartctl |
Путь к бинарю smartctl, если не в PATH |
SENSORS_BIN |
sensors |
Путь к бинарю sensors (lm_sensors), если не в PATH; опционально |
NVIDIA_SMI_BIN |
nvidia-smi |
Путь к бинарю nvidia-smi, если не в PATH; опционально |
TEMP_WARN_C, TEMP_CRIT_C |
50, 60 |
Пороги температуры дисков (°C) для «внимание»/«критично» |
CPU_TEMP_WARN_C, CPU_TEMP_CRIT_C |
80, 90 |
Пороги температуры CPU (°C) |
GPU_TEMP_WARN_C, GPU_TEMP_CRIT_C |
75, 85 |
Пороги температуры GPU (°C) |
APP_NAME, ICON_NAME, URGENCY |
SystemMonitor, utilities-system-monitor, critical |
Параметры desktop-уведомления |
Использование:
# Опрос дисков/CPU/GPU + запись в SQLite + HTML-отчёт
sudo ./system_monitor.sh
# Только пересобрать HTML-отчёт из уже накопленных данных, без опроса
sudo ./system_monitor.sh -rВажно: для чтения SMART-атрибутов smartctl обычно требует root — крон-запись
нужно добавлять в root-crontab (sudo crontab -e), а не в обычный пользовательский.
Опрос CPU/GPU root не требует, но так как весь скрипт обычно запускается под
root ради дисков, они опрашиваются в том же (привилегированном) запуске.
Расписание — systemd timer (рекомендуется):
Юнит-файлы лежат в systemd/system-monitor.service и systemd/system-monitor.timer.
Таймер срабатывает в 00/06/12/18 часов, а благодаря Persistent=true пропущенный
слот (машина была выключена) выполняется при первой же загрузке — и не более одного
раза за интервал. Обычный cron 0 */6 пропуски не догоняет: если компьютер
включают, скажем, в 18:30 и выключают до полуночи, замеры не будут попадать в базу
вообще.
sudo cp systemd/system-monitor.service systemd/system-monitor.timer /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now system-monitor.timer
# Проверка
systemctl list-timers system-monitor.timer
journalctl -u system-monitor.service -n 20Если раньше скрипт стоял в root-crontab — строку 0 */6 * * * …system_monitor.sh
оттуда нужно убрать (sudo crontab -e), иначе опрос будет идти дважды.
Cron (альтернатива, без догоняния пропусков):
0 */6 * * * /home/esimych/esimych-docs/mytasks/system_monitor.sh >/dev/null 2>&1Актуальный отчёт всегда доступен по стабильному пути system_reports/latest.html;
предыдущие версии хранятся как system_reports/system_monitor-<timestamp>.html
(не более REPORT_KEEP штук).
Рекурсивная перекодировка текстовых файлов из Windows-1251 в UTF-8.
Возможности:
- Пропускает бинарные и уже UTF-8 файлы
- Атомарная замена через временный файл (
mktemp) - Сохраняет права и владельца файла
- Режим
--dry-run— только показывает, что будет перекодировано
Использование:
./cp1251_to_utf8.sh <каталог>
./cp1251_to_utf8.sh <каталог> --dry-run
./cp1251_to_utf8.sh -hРекурсивное удаление файлов по расширению с логированием.
Опции:
| Опция | Описание |
|---|---|
-d DIR |
Каталог для обработки (обязательно) |
-e EXT |
Расширение без точки, например: tmp, log, bak |
--dry-run |
Показать файлы без удаления |
-h |
Справка |
Использование:
./delete_by_ext.sh -d /tmp -e log --dry-run
./delete_by_ext.sh -d ./downloads -e tmpЛог пишется в logs/delete_by_ext-YYYY-MM-DD-HH-MM-SS.jsonl; хранятся
10 последних JSONL-файлов.
Рекурсивная конвертация DOC / DOCX / RTF / ODT → FB2.
Цепочки конвертации:
docx,odt→ pandoc → fb2doc,rtf→ LibreOffice → odt → pandoc → fb2- Fallback для
rtf: pandoc напрямую, затемunrtf→ txt → pandoc - Fallback для
doc:antiword/catdoc/wvText→ txt → pandoc
Ключевые опции:
| Опция | Описание |
|---|---|
-d, --dir DIR |
Исходный каталог (обязательно) |
-o, --out OUTDIR |
Выходной каталог |
-n, --dry-run |
Без изменений, только показать |
--overwrite |
Перезаписывать существующие FB2 |
--skip-newer |
Пропускать, если FB2 новее источника |
--exclude PATTERN |
Исключить файлы по маске (можно несколько раз) |
| `--only doc | docx |
--author NAME |
Принудительно установить автора |
--toc / --no-toc |
Добавить/убрать оглавление |
| `--log-level info | error` |
Использование:
./doc2fb2.sh -d /книги --dry-run
./doc2fb2.sh -d /книги -o /fb2 --overwrite --log-level errorЛог: JSONL-файл doc2fb2_*.jsonl. Логи старше 10 дней удаляются автоматически.
Определяет текущий внешний IP-адрес и ведёт его историю.
Возможности:
- Запрашивает внешний IP у настраиваемого URL-сервиса через
curl - Сохраняет текущий IP в файл
- Добавляет запись с временной меткой, если полученного IP ещё нет в файле истории
- Все пути и URL задаются через конфиг-файл
conf/getip.conf
Конфигурация (conf/getip.conf):
| Параметр | По умолчанию |
|---|---|
IP_SERVICE_URL |
https://icanhazip.com |
IP_FILE |
/var/downloads/clouddata/nextcloud/work/ip.txt |
IP_HISTORY_FILE |
/var/downloads/clouddata/nextcloud/work/ip_history.txt |
Примеры совместимых сервисов: https://api.ipify.org, https://ifconfig.me/ip, https://checkip.amazonaws.com
Использование:
./getip.shСкрипт установки и настройки принтера HP LaserJet P1005 на Garuda Linux (Arch-based).
Возможности:
- Устанавливает необходимые пакеты:
hplip,system-config-printer, CUPS - Настраивает службы CUPS
- Запуск без аргументов =
--all(выполнить все шаги) - JSONL-логирование в файл
hp_p1005_garuda_YYYY-MM-DD-HH-MM-SS.jsonl
Использование:
sudo ./hp_p1005_garuda.sh
sudo ./hp_p1005_garuda.sh --allПереименовывает расширения файлов в нижний регистр (.JPG → .jpg, .MP3 → .mp3).
Режимы:
| Режим | Описание |
|---|---|
--dry-run |
Показать, что будет переименовано |
--apply |
Реально переименовать файлы |
Использование:
./lowercase_ext.sh --dry-run /path/to/dir
./lowercase_ext.sh --apply /path/to/dirПоказывает прогресс в процентах при обработке.
Рекурсивно обрабатывает MHT/MHTML и CHM в указанном каталоге.
Возможности:
- Умная автодетекция кодировки: UTF-8, CP1251, KOI8-R, Latin-1
- Извлекает текст и структуру из HTML-части архива
- MHT/MHTML конвертирует через
pandocв FB2 рядом с исходником; после успешной обычной конвертации удаляет исходный веб-архив - Для CHM не выполняет конвертацию, а удаляет одноимённый FB2, если он существует
--overwriteразрешает перезаписать существующий FB2--dry-runне удаляет исходный MHT/MHTML и только имитирует удаление FB2 для CHM, но текущая реализация всё равно запускаетpandocи может записать FB2- JSONL-логирование в папку
logs/
Требования: Python 3, pandoc
Использование:
python3 mht_to_fb2.py /архивы
python3 mht_to_fb2.py /архивы --overwrite
python3 mht_to_fb2.py /архивы --dry-runОбёртка для запуска Python-проекта music_downloader.
Порядок работы:
- Активирует виртуальное окружение (
venv) в папкеmusic_downloader/, если оно существует. - Запускает
music_downloader.pyи сохраняет его код выхода. - Деактивирует venv; при коде выхода загрузчика
0запускаетsplit_by_dash.sh, если он найден. - Логирует результат и возвращает код выхода загрузчика.
Конфигурация загрузчика: в music_downloader/music_downloader.json общий непустой список download_paths задаётся в корне JSON, рядом с sites. Каждый трек любого моста сохраняется во все указанные папки. path задаёт каталог, max_size_mb — лимит его общего размера (по умолчанию 1024 МБ), а не квоту отдельного сайта. При переходе со старого формата перенесите общий список из сайта в корень JSON и удалите копии из sites: локальные настройки папок мостов больше не используются.
Папки и лимиты проверяются до обращения к сайтам и перед сохранением каждого трека. Размер проверяется в байтах, в том числе для каждого блока потока без Content-Length. Если трек не помещается хотя бы в одну папку, загрузчик удаляет его незавершённые .part, не записывает его в БД и завершает работу с кодом 2, без запуска сортировщика. Уже сохранённые треки остаются. Контроль рассчитан на один процесс загрузчика; параллельные записи других программ требуют квот файловой системы. Отсутствующий/пустой глобальный список, отсутствующая папка или уже достигнутый лимит также завершают запуск с кодом 2. Ошибки отдельных сайтов и треков логируются, но не обязательно меняют итоговый код выхода. При отсутствии файла конфигурации текущий загрузчик возвращает 0 без скачивания — подготовьте JSON до запуска обёртки. Пути сортировщика настраиваются отдельно.
Jamendo для категории / использует официальный API последних треков вместо HTML главной страницы, выбирая только записи с разрешённым скачиванием. Пустые результаты и ошибки явно логируются; подробности — в README загрузчика.
Мосты shazam_com (мировой Top 200; аудио ищется по точному совпадению в Sefon и LMusic) и mychords_net (новинки, аудио из встроенного плеера xpleer) получают ссылку на файл только перед скачиванием нового трека (resolve_track), поэтому уже скачанные позиции не вызывают лишних запросов.
Логи: в общей папке logs/ обёртка пишет music_downloader-<timestamp>.jsonl (оставляет последние 10 файлов), Python-движок — music_downloader_engine-<timestamp>.jsonl (очистка по глобальным log_cleanup_days и log_max_files, по умолчанию 10 дней и 10 файлов). Текстовый вывод загрузчика и сортировщика записывается в logs/cron_execution.log.
Использование:
./music_downloader.shПодробнее — в README загрузчика. Каталог
music_downloader/входит в этот репозиторий как обычная папка, без вложенного.gitи без submodule. Код, драйверы, тесты и шаблон конфигурации версионируются вместе сmytasks; рабочий JSON, БД, логи иvenvисключены правиламиmusic_downloader/.gitignore. CI загрузчика находится в.github/workflows/music-downloader.yml.
Заменяет строку в текстовых файлах (поиск + замена через sed).
По умолчанию заменяет windows-1251 → utf-8 во всех текстовых файлах каталога.
Если каталог не указан, используется текущий (.).
Опции:
| Опция | Описание |
|---|---|
-d, --dry-run |
Показать файлы без изменений |
-n, --no-recursive |
Только текущий каталог (без подкаталогов) |
-h, --help |
Справка |
Использование:
./replace_encoding.sh /path/to/dir
./replace_encoding.sh /path/to/dir "старая строка" "новая строка"
./replace_encoding.sh -d /path/to/dirСинхронизация файлов на основе конфиг-файла conf/sources.conf с помощью rsync.
Формат конфига (каждая строка):
источник | назначение | rsync-аргументы
Опции:
| Опция | Описание |
|---|---|
-c |
Указать альтернативный конфиг |
-n |
Режим dry-run (передаёт --dry-run в rsync) |
-E |
Разобрать кавычки в аргументах rsync через xargs, без выполнения eval |
-h |
Справка |
Логи: JSONL-файл logs/sources_YYYY-MM-DD-report.jsonl. Логи старше 10 дней удаляются автоматически.
Использование:
./sources.sh
./sources.sh -n
./sources.sh -c /path/to/my.confРаскладывает файлы по папкам-исполнителям по шаблону Исполнитель - Название.
Алгоритм:
- Ищет
-(или–,—) в имени файла - Часть до разделителя становится именем подпапки
- Файл перемещается в эту подпапку; при конфликте имён добавляет суффикс
.1,.2…
Источники каталогов:
- Аргумент командной строки:
./split_by_dash.sh /путь/к/папке - Конфиг-файл
conf/split_by_dash.conf(список папок, по одной на строку)
Перенос результата (опционально):
- Строка
DEST_DIR=/путь/к/каталогувconf/split_by_dash.confзадаёт каталог, куда переносятся все получившиеся папки-исполнители после разбора - Если папки с таким именем в
DEST_DIRещё нет — она переносится целиком - Если такая папка уже существует — файлы объединяются в неё (конфликты имён также разрешаются суффиксом
.1,.2…), а опустевший источник удаляется - Без
DEST_DIRпапки остаются внутри исходного каталога - Все операции переноса/объединения логируются
Логи: JSONL-файл в logs/. Хранятся 10 последних файлов.
Использование:
./split_by_dash.sh /path/to/music
./split_by_dash.sh # использует conf/split_by_dash.confРаспаковывает архивы *.fb2.zip, извлекая .fb2-файлы в тот же каталог.
Возможности:
- Рекурсивный поиск
*.fb2.zip - Конфликты имён разрешаются суффиксами
_1,_2… - Режим
--dry-run - JSONL-логирование
Опции:
| Опция | Описание |
|---|---|
-p, --path DIR |
Корневая папка для поиска (обязательно) |
--dry-run |
Только показать действия |
--log FILE |
Путь к JSONL-логу |
-h, --help |
Справка |
Использование:
./unpack-fb2zip.sh -p /книги
./unpack-fb2zip.sh -p /книги --dry-run| Скрипт | Основные зависимости |
|---|---|
all_to_mp3.sh |
ffmpeg, ffprobe, find, xargs |
auto_delete_old_files.sh |
POSIX shell, find, sort, awk, sed |
btrfs_monitor.sh |
Bash, btrfs, findmnt, awk; опц.: notify-send |
check-store-mount.sh |
Bash, mountpoint, timeout; опц.: notify-send |
cloud_backup.sh |
Bash, wg-quick, sshpass, ssh; удалённо: docker, tar, один из zstd/pigz/gzip |
cloud_backup_qnap.sh |
Entware Bash, wireguard-go, ncat, xxd, ssh; удалённо: docker, tar, nc, один из zstd/pigz/gzip |
cp1251_to_utf8.sh |
iconv, file, find |
delete_by_ext.sh |
find |
doc2fb2.sh |
pandoc, libreoffice; опц.: unrtf, antiword, catdoc, wvText |
getip.sh |
curl |
hp_p1005_garuda.sh |
Arch/Garuda, pacman, lsusb, systemctl, CUPS (lp), system-config-printer |
lowercase_ext.sh |
Bash, find, mv |
mht_to_fb2.py |
Python 3, pandoc |
music_downloader.sh |
Bash, Python 3, локальный проект music_downloader/; после успеха — split_by_dash.sh |
replace_encoding.sh |
Bash, grep, sed |
sources.sh |
POSIX shell, rsync, xargs |
split_by_dash.sh |
POSIX shell, find, mv, sed |
system_monitor.sh |
Bash, smartctl (root), jq, sqlite3, lsblk; опц.: sensors, nvidia-smi, notify-send |
unpack-fb2zip.sh |
Bash, find, unzip |
Каталог tests/ содержит отдельные Bats-тесты для актуальных shell-скриптов
и общий all_scripts_syntax.bats, который проверяет синтаксис каждого
корневого *.sh по его шебангу. Внешние команды стабируются через временный
PATH; для system_monitor.sh настоящими остаются sqlite3 и jq.
Установка и полный запуск:
git clone --depth 1 https://github.com/bats-core/bats-core.git /tmp/bats-core
sudo /tmp/bats-core/install.sh /usr/local
bats --print-output-on-failure testsЗапуск одного набора:
bats --print-output-on-failure tests/cloud_backup_qnap.batsGitHub CI:
- Workflow:
.github/workflows/bats-tests.yml - Запускается автоматически на
pushиpull_request
При изменении корневых *.sh или *.py запускается workflow статического
анализа.
GitHub CI: .github/workflows/shellcheck.yml
| Шаг | Что проверяется |
|---|---|
| ShellCheck error | Ошибки и предупреждения уровня error (блокируют merge) |
| ShellCheck style | Предупреждения уровня style (предпочтительный [[ ]], braces, case *)…) |
| bash/sh syntax | bash -n или sh -n по shebang для каждого .sh |
| Unbraced variable scan | grep-сканнер небракетированных $VAR в локальном bash-коде (не в remote-shell строках) |
| Python syntax | python3 -m py_compile для каждого .py в корне |
Workflow запускается при изменении *.sh или *.py.
Перед локальной отправкой изменений shell-скрипта дополнительно выполняйте все опциональные проверки ShellCheck:
bash -n path/to/script.sh
shellcheck -S style path/to/script.sh
shellcheck -o all path/to/script.sh