1
0
Fork 0
Виртуальный накопитель в памяти
Find a file
2026-05-07 18:42:26 +03:00
.gitignore Инициализация / 20 2026-05-07 18:42:26 +03:00
bash.jpg ./bash.jpg / 3,7K 2026-05-07 18:42:26 +03:00
COPYING Копирование / 1,2K 2026-05-07 18:42:26 +03:00
do-backup.sh ./do-backup.sh / 3,5K 2026-05-07 18:42:26 +03:00
do-restore.sh ./do-restore.sh / 2,8K 2026-05-07 18:42:26 +03:00
gif-animation.sh ./gif-animation.sh / 1,4K 2026-05-07 18:42:26 +03:00
README.md Описание проекта / 18K 2026-05-07 18:42:26 +03:00
README2.md Описание проекта / 756 2026-05-07 18:42:26 +03:00
repo_copy.sh ./repo_copy.sh / 7,0K 2026-05-07 18:42:26 +03:00
repo_create.sh ./repo_create.sh / 8,0K 2026-05-07 18:42:26 +03:00
rollup-the-list.sh ./rollup-the-list.sh / 1,9K 2026-05-07 18:42:26 +03:00
tmpfs-to-fstab.sh ./tmpfs-to-fstab.sh / 1,5K 2026-05-07 18:42:26 +03:00

Файловые операции в ОЗУ

Для тестирования скриптов Bash хорошо подходит оперативная память, чтобы жёсткий диск не уставал. Но Bash не умеет писать данные в оперативную память — только в файлы. Зато можно выделить область в ОЗУ и работать с ней как со временной файловой системой. Плюсы и минусы такого подхода видны сразу — высокая скорость работы и короткий период хранения данных — до первой перезагрузки. Поэтому для работы с такой файловой системой пригодятся два вспомогательных скрипта для сохранения и восстановления данных. Третий скрипт для добавления самой файловой системы в конфигурацию ОС и дополнительный скрипт для свёртки списка строк. На закуску скрипт для создания анимации GIF из скриншотов, потому что иногда в моём блоге встречаются иллюстрации. Всего пять скриптов.


Сохранение данных в архивный файл


Ленточный архив TAR (tape archive) без сжатия удобно использовать, потому что скорость работы с ним сопоставима со скоростью чтения или записи файлов, при этом все эти файлы собираются в единый архив. Дополнительное сжатие GZIP использовать не выгодно, особенно при частом выполнении, потому что оно занимает много времени и даёт слабый эффект при большом количестве изображений и музыки. Краткие архивы не содержат папки .git и .idea — благодаря первой из них, размер краткого архива меньше в два раза.

#!/bin/bash
echo "Сохранение каталогов проектов на временном накопителе в архивные файлы 'tar' без сжатия."
#-----------------------------------------------------------------------------------------------;
prompt="Полный/Краткий (П/К): " && read -erp "$prompt" line && echo -ne "\033[1A$prompt\033[K"
case "${line,,}" in f | g | а | п) echo "ПОЛНЫЙ" && capacity=full ;; *) echo "КРАТКИЙ" ;; esac
#-----------------------------------------------------------------------------------------------;
ramdisk="$HOME/ramdisk"       # временный накопитель в ОЗУ
backup="$HOME/ramdisk-backup" # каталог для архивов
folder="ramdisk-$(date -I)"   # каталог за текущую дату
time_ms="$(date '+%s%3N')"    # текущее время в миллисекундах
#-----------------------------------------------------------------------------------------------;
# сохранение каталога в архив
function do_backup {
  # переход в каталог проекта, получение массива файлов и каталогов в отсортированном виде, выход в случае ошибки
  cd "$1" && readarray -t content < <(find . -mindepth 1 -maxdepth 1 -type f,d -printf "%f\n" | sort) || return 1
  # проект должен быть непустым, чтобы архив был непустой, иначе выход из функции
  [ ${#content[@]} == 0 ] && return 1
  # добавление суффикса для каталога полных архивов, или массива исключений для файлов кратких архивов
  [ -n "$capacity" ] && folder="${folder}-${capacity}" || exclude=(--exclude='.git' --exclude='.idea')
  # создание каталога для архивов, путь и название для файла, получение временного суффикса
  mkdir -p "$backup/$folder" 2>/dev/null && path_name="$backup/$folder/$1" && tmp="$RANDOM"
  # создание архива текущего каталога, кроме массива исключений, выход в случае ошибки
  tar --create --file="$path_name.$tmp.tar" "${exclude[@]}" "${content[@]}" || return 1
  # удаление старого файла и переименование созданного нового файла
  rm -f "$path_name.tar" && mv "$path_name.$tmp.tar" "$path_name.tar"
  echo "Сохранение: $folder/$1.tar $(stat -c %s "$path_name.tar" | numfmt --to=iec --round=nearest)"
}
export -f do_backup && export capacity ramdisk backup folder
# переход на временный накопитель
cd "$ramdisk" || exit 1
# поиск всех каталогов на одном уровне с текущим и запуск функции сохранения для каждого из них
find . -mindepth 1 -maxdepth 1 -type d -printf 'do_backup "%P"\0' | xargs -n1 -0 -P0 bash -c
#-----------------------------------------------------------------------------------------------;
# замер продолжительности выполнения в миллисекундах, пересчёт в минуты, секунды и миллисекунды
tms="$(($(date '+%s%3N') - time_ms))" && min="$((tms / 1000 / 60))" && sec="$((tms / 1000 % 60))"
ms="$((tms % 1000))" && printf 'Общее время выполнения: %02d:%02d.%03d мс.\n' "$min" "$sec" "$ms"

Вывод

Сохранение каталогов проектов на временном накопителе в архивные файлы 'tar' без сжатия.
Полный/Краткий (П/К): КРАТКИЙ
Сохранение: ramdisk-2025-05-23/empty.tar 220K
Сохранение: ramdisk-2025-05-23/project_blog.tar 34M
Сохранение: ramdisk-2025-05-23/music.tar 493M
Сохранение: ramdisk-2025-05-23/pictures.tar 613M
Общее время выполнения: 00:01.367 мс.

Восстановление данных из архивного файла


Восстановление данных выполняем в отдельную папку и далее перемещаем вручную по необходимости.

#!/bin/bash
echo "Восстановление каталогов проектов на временный накопитель из архивных файлов 'tar'."
#-----------------------------------------------------------------------------------------------;
prompt="Полный/Краткий (П/К): " && read -erp "$prompt" line && echo -ne "\033[1A$prompt\033[K"
case "${line,,}" in f | g | а | п) echo "ПОЛНЫЙ" && capacity=full ;; *) echo "КРАТКИЙ" ;; esac
#-----------------------------------------------------------------------------------------------;
ramdisk="$HOME/ramdisk"       # временный накопитель в ОЗУ
backup="$HOME/ramdisk-backup" # каталог для архивов
folder="ramdisk-$(date -I)"   # каталог за текущую дату
time_ms="$(date '+%s%3N')"    # текущее время в миллисекундах
#-----------------------------------------------------------------------------------------------;
# добавление суффикса для каталога полных архивов
[ -n "$capacity" ] && folder="${folder}-${capacity}"
# проверка существования каталога архивов
[ -d "$backup/$folder" ] || exit 1
# восстановление каталога из архива
function do_restore {
  dir="${1%.tar}"
  # удаление старого каталога и создание нового
  rm -rf "${dir:?}" && mkdir "$dir" 2>/dev/null
  # переход в новый каталог
  cd "$dir" || return 1
  # копирование данных из архивного файла
  tar --extract --file="$backup/$folder/$1"
  echo "Копирование: $folder/$dir $(du -sb | grep -oP '.*(?=\s+.*)' | numfmt --to=iec --round=nearest)"
}
export -f do_restore && export backup folder
# каталог для восстановления архивов за текущую дату
mkdir -p "$ramdisk/$folder" 2>/dev/null
# переход в новый каталог
cd "$ramdisk/$folder" || exit 1
# поиск всех файлов "tar" в архивном каталоге и запуск функции восстановления для каждого из них
find "$backup/$folder" -type f -name "*.tar" -printf 'do_restore "%P"\0' | xargs -n1 -0 -P0 bash -c
#-----------------------------------------------------------------------------------------------;
# замер продолжительности выполнения в миллисекундах, пересчёт в минуты, секунды и миллисекунды
tms="$(($(date '+%s%3N') - time_ms))" && min="$((tms / 1000 / 60))" && sec="$((tms / 1000 % 60))"
ms="$((tms % 1000))" && printf 'Общее время выполнения: %02d:%02d.%03d мс.\n' "$min" "$sec" "$ms"

Вывод

Восстановление каталогов проектов на временный накопитель из архивных файлов 'tar'.
Полный/Краткий (П/К): КРАТКИЙ
Копирование: ramdisk-2025-05-23/empty 191K
Копирование: ramdisk-2025-05-23/project_blog 33M
Копирование: ramdisk-2025-05-23/music 493M
Копирование: ramdisk-2025-05-23/pictures 612M
Общее время выполнения: 00:00.785 мс.

Добавление tmpfs в конфигурацию fstab


Добавляем временную файловую систему для ОЗУ в конфигурацию файловых систем с точкой монтирования в каталоге /mnt и со ссылкой в домашнем каталоге пользователя. Для этого сначала с правами обычного пользователя получаем путь к его домашнему каталогу и затем с правами суперпользователя вносим изменения в конфигурацию файловых систем — либо в обратном порядке — запускаем bash с правами суперпользователя и получаем права обычного пользователя с помощью команды sudo -u \#1000.

#!/bin/bash
# имя пользователя и привилегии суперпользователя
if [ "$UID" == 1000 ]; then
  echo "Добавление временного файлового хранилища для ОЗУ в таблицу файловых систем."
  # запуск нового процесса и завершение текущего процесса
  exec sudo bash "$0" "${USER}" && exit 0
elif [ "$UID" != 0 ] || [ -z "$1" ]; then
  echo "Скрипт следует выполнять с правами обычного пользователя." && exit 1
fi
# список временных файловых систем с точкой монтирования в каталоге '/mnt'
lines+=("# временное файловое хранилище для ОЗУ / temporary file storage for RAM")
lines+=("tmpfs  /mnt/ram  tmpfs  size=16G,nosuid,mode=0755,gid=1000,uid=1000  0  0")
lines+=("tmpfs  /mnt/cache  tmpfs  size=8G,nosuid,mode=0755,gid=1000,uid=1000  0  0")
# добавление строк в таблицу файловых систем и вывод в консоль для наглядности
printf '%s\n' "${lines[@]}" | tee -a "/etc/fstab"
# создание символических ссылок в каталоге пользователя
ln -fsv "/mnt/ram" "/home/$1/ramdisk"
ln -fsv "/mnt/cache" "/home/$1/.cache"
# свёртка таблицы, исключение дубликатов строк
./rollup-the-list.sh "/etc" "fstab" "fstab"

Свёртка списка / журнала истории


Скрипт для сохранения списка последних строк без повторов в том же порядке. Сначала сортируем полученные файлы по датам и читаем строки в хронологическом порядке. Затем добавляем колонку с номерами строк и сортируем список таким образом, чтобы повторяющиеся записи шли друг за другом в хронологическом порядке по убыванию. После этого исключаем дубликаты и сортируем список обратно по номерам строк. Затем отбрасываем колонку с номерами и сохраняем полученный результат.

#!/bin/bash
echo "Свёртка списка. Исключение дубликатов строк и сохранение последних записей в том же порядке."
# следует указывать либо все три параметра, либо не указывать вовсе
# без параметров отрабатывает свёртка файлов журнала истории bash
# $1 — каталог, $2 — шаблон файлов, $3 — новый файл
if [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ]; then
  folder="$HOME" && pattern=".bash_history*" && file=".bash_history"
else
  folder="$1" && pattern="$2" && file="$3"
fi
printf 'каталог="%s", шаблон="%s", файл="%s"\n' "$folder" "$pattern" "$file"
# поиск всех файлов по заданному шаблону "pattern" в каталоге "folder", сортировка по датам изменения и чтение,
# добавление колонки с номерами, сортировка со второй колонки и с первой колонки в обратном порядке, исключение
# повторов строк без учёта первой колонки, сортировка по номерам, обрезка со второй колонки и сохранение в файл
find "$folder" -maxdepth 1 -type f -name "$pattern" -printf 'cat "%p" #%C@\n' | sort -k3 | xargs -n1 \
  -d'\n' -r bash -c | nl | sort -k2 -k1nr | uniq -f1 | sort -n | cut -f2 >"$folder/tmp_${file}_tmp"
# поиск и удаление всех файлов по заданному шаблону "pattern" в каталоге "folder"
find "$folder" -maxdepth 1 -type f -name "$pattern" -exec rm -fv {} \;
# переименование созданного файла в новый файл
mv -vf "$folder/tmp_${file}_tmp" "$folder/$file"

Анимация GIF из скриншотов


Три шага преобразований лучше выполнять по отдельности, чтобы визуально убедиться в результатах. Сначала создаём серию скриншотов, выкидываем лишние или одинаковые, если таковые попались. После этого вырезаем релевантную часть из всех изображений и сохраняем в отдельные файлы. Затем объединяем части в один анимационный файл.

#!/bin/bash
# каталог для скриншотов должен существовать, создание при необходимости
folder="$HOME/ramdisk/screenshot" && mkdir -p "$folder" 2>/dev/null
#-----------------------------------------------------------------------------------------------;
# создание 90 скриншотов по 5 штук в секунду и сохранение их в каталоге
for i in {01..90}; do
  xfce4-screenshooter --fullscreen --save "$folder/screenshot${i}.gif" &
  sleep 0.2
done
#-----------------------------------------------------------------------------------------------;
# обрезка изображений и сохранение релевантных частей в этом же каталоге, Ш×В нового изображения и Ш×В отступ
for i in {01..90}; do
  convert -verbose -strip "$folder/screenshot${i}.gif" -crop 640x480+320+230 +repage "$folder/animation${i}.gif"
done
#-----------------------------------------------------------------------------------------------;
# анимация в бесконечном цикле из полученных изображений с задержкой 0.5 секунды на кадре и ещё 3 секунды на последнем кадре
convert -verbose -loop 0 -delay 50 "$folder"/animation{01..90}.gif -delay 300 "$folder"/animation90.gif "$folder"/animation.gif