School21 Bash: Part 1 «Генератор файлов» — проект от требований до готового решения

Курс помогает спроектировать и реализовать Bash-скрипт генерации папок и файлов по строгим правилам именования, размера и логирования. Включает декомпозицию на файлы, валидацию параметров, контроль свободного места и проверку результата под требования задания.

1. Разбор требований задания и структура проекта src/0x с main.sh

Разбор требований задания и структура проекта src/0x с main.sh

Контекст курса и цель

В этом курсе ты реализуешь Bash-скрипт генератора папок и файлов по строгому ТЗ School21. Эта статья — стартовая: мы разберём требования, договоримся о структуре проекта в src/0x, определим роли файлов и зафиксируем, какие проверки обязаны быть в каждом скрипте.

К концу статьи у тебя будет:

  • понимание, что именно требуется сделать;
  • список обязательных проверок входных параметров;
  • согласованная структура каталогов и файлов в src/0x;
  • каркас декомпозиции (какие функции и модули понадобятся).
  • Разбор требований задания

    Скрипт запускается с 6 параметрами:

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

    Параметры запуска и проверки

    Параметр 1 — абсолютный путь

    Требование: абсолютный путь до директории, где нужно создать структуру.

    Проверки:

  • параметр передан;
  • путь начинается с /;
  • директория существует и доступна на запись.
  • Полезные команды/утилиты:

  • [[ "path" ]] — существует ли директория;
  • [[ -w "\ge 1n" =~ ^[1-9][0-9]* (или допускай 0kb, если разрешишь — но это обычно бессмысленно для генератора);
  • числовая часть .
  • Для создания файла нужного размера удобно использовать truncate (если доступен) или dd.

    Справка:

  • truncate — GNU Coreutils
  • dd — GNU Coreutils
  • Правила формирования имён

    Требование к именам папок и файлов:

  • “имя” (часть до даты) должно быть длиной минимум 4 символа;
  • затем добавляется _DDMMYY (дата запуска скрипта), например _021121;
  • итоговые примеры из ТЗ:
  • - ./aaaz_021121/ - ./aaazzzz_021121

    Практические выводы для реализации:

  • У тебя всегда есть две части имени:
  • - базовая часть из букв параметра; - суффикс даты, общий для всех сущностей текущего запуска.
  • Если набор букв короткий (например a или az), базовую часть надо нарастить повторами, но без нарушения порядка.
  • Расширение файла — отдельная часть, её тоже нужно генерировать по правилам.
  • Команда для даты:

  • date +%d%m%y
  • Справка:

  • date — GNU Coreutils
  • Генерация структуры на диске

    Требование:

  • в директории из Параметра 1 создать Параметр 2 папок;
  • в каждой папке создать Параметр 4 файлов;
  • имена и размеры соответствуют правилам.
  • Важное следствие: скрипт должен корректно обрабатывать конфликты имён. Самая безопасная стратегия:

  • генерировать имя;
  • если путь уже существует — модифицировать базовую часть (например, добавляя ещё один символ последней буквы в порядке параметра) и пробовать снова.
  • Ограничение по свободному месту

    Требование:

  • остановить работу, если в файловой системе (в разделе /) останется 1 Гб свободного места.
  • Что значит “останется 1 Гб”:

  • нужно проверять свободное место на разделе / перед созданием очередной сущности (или хотя бы перед созданием очередного файла, так как именно файлы занимают место);
  • когда свободного места становится GiB — прекращать генерацию и корректно завершаться.
  • Технически удобно использовать:

  • df -k / и брать доступные килобайты;
  • сравнивать с порогом 110241024 килобайт.
  • Справка:

  • df — GNU Coreutils
  • Лог-файл

    Требование:

  • записать лог по всем созданным папкам и файлам;
  • для каждой записи:
  • - полный путь; - дата создания; - для файлов — размер.

    Практичные решения:

  • путь: хранить как строку, которую ты используешь при создании ("name");
  • дата создания:
  • - чаще всего достаточно времени выполнения операции: date "+%Y-%m-%d %H:%M:%S"; - если пытаться брать birth time из stat, он может быть недоступен на некоторых ФС, поэтому для учебного проекта безопаснее логировать “время создания” как “время, когда скрипт создал объект”.
  • размер:
  • - логируй в килобайтах из входного параметра; - дополнительно (по желанию) можно проверить фактический размер через stat.

    Справка:

  • stat — GNU Coreutils
  • !Блок-схема, показывающая основные этапы работы скрипта и точки проверок

    Требования к структуре проекта School21

    По условиям:

  • все Bash-скрипты лежат в папке src;
  • для каждого задания создаётся папка 0x, где x — номер задания;
  • основной файл сценария для каждого задания называется main.sh;
  • скрипты должны быть декомпозированы и разбиты на несколько файлов;
  • во всех скриптах — проверки некорректного ввода.
  • Для Part 1 логично сделать src/01.

    Предлагаемая структура src/01

    Ниже структура, которая помогает пройти проверки и удобно развивать проект:

    Зачем так:

  • main.sh — точка входа: парсит параметры, вызывает валидацию, запускает генерацию;
  • lib/validate.sh — все проверки аргументов и понятные сообщения об ошибках;
  • lib/naming.sh — генерация имён папок/файлов по правилам (порядок букв, минимум 4 символа, суффикс даты, расширение);
  • lib/fs.sh — всё про файловую систему: проверка свободного места, создание директорий/файлов;
  • lib/log.sh — единый формат логирования;
  • lib/generator.sh — “оркестратор” циклов: создать N папок, в каждой создать M файлов;
  • conf/constants.sh — пороги и константы (например, лимит свободного места, лимиты длины параметров).
  • Минимальные контракты модулей

    Чтобы декомпозиция была рабочей, зафиксируй (для себя и для ревью) “кто за что отвечает”. Например:

  • validate.sh
  • 1. Проверяет количество аргументов (должно быть 6). 2. Проверяет формат и ограничения каждого параметра. 3. При ошибке печатает понятное сообщение в stderr и завершает exit 1.
  • naming.sh
  • 1. Получает набор букв и минимальную длину базовой части. 2. Возвращает базовое имя, которое: 1. состоит только из разрешённых букв; 2. включает каждую букву хотя бы раз; 3. сохраняет порядок букв; 4. имеет длину минимум 4. 3. Добавляет суффикс даты _DDMMYY. 4. Для файла: отдельно генерирует имя и расширение.
  • fs.sh
  • 1. Проверяет свободное место на /. 2. Создаёт директории. 3. Создаёт файл заданного размера (например, через truncate -s (cd "{BASH_SOURCE[0]}")" && pwd)"

    shellcheck source=lib/validate.sh

    source "SCRIPT_DIR/lib/generator.sh"

    shellcheck source=lib/log.sh

    source "@"

    init_log

    run_generator "@" bash bash src/01/main.sh /opt/test 4 az 5 az.az 3kb `

    должен:

  • создать 4 папки в /opt/test, имена по правилам с суффиксом _DDMMYY;
  • в каждой папке создать 5 файлов с именем и расширением по правилам;
  • каждый файл имеет размер 3KB;
  • при достижении порога свободного места (1 GiB на /`) — остановиться;
  • записать лог по всем созданным объектам.
  • Что будет дальше в курсе

    В следующих статьях логично двигаться так:

  • строгая валидация параметров и единый стиль ошибок;
  • генерация имён с сохранением порядка букв и минимальной длиной;
  • создание файлов нужного размера и проверка свободного места;
  • логирование и итоговая сборка в целостное решение.
  • 2. Парсинг 6 параметров и строгая валидация ввода в Bash

    Парсинг 6 параметров и строгая валидация ввода в Bash

    Зачем нужна строгая валидация именно в этом проекте

    В предыдущей статье мы зафиксировали требования ТЗ и структуру src/01 с декомпозицией по модулям. Следующий шаг — сделать так, чтобы main.sh никогда не запускал генерацию, если входные параметры некорректны.

    Почему это критично:

  • генератор создаёт много директорий и файлов, и ошибка в аргументах быстро превращается в мусор на диске
  • часть требований завязана на формат строк и лимиты (длины, kb, точка в имени файла)
  • на ревью в School21 обычно проверяют, что скрипт корректно отказывает при неправильном вводе, а не “пытается угадать”
  • !Схема пайплайна валидации аргументов

    Парсинг параметров: позиционные аргументы вместо флагов

    По ТЗ скрипт запускается строго с 6 параметрами в фиксированном порядке:

    Поэтому:

  • удобнее использовать позиционные параметры 6
  • getopts здесь не обязателен, потому что он решает задачу флагов (-p, -n), а у нас жёсткий порядок
  • Важно: валидация должна проверять и количество, и формат, и ограничения.

    Справка по позиционным параметрам и поведению Bash: Bash Reference Manual

    Реализация модуля валидации src/01/lib/validate.sh

    Ниже — рабочий шаблон, который можно прямо положить в src/01/lib/validate.sh. Он не генерирует ничего, только проверяет ввод и сообщает понятную ошибку.

    Базовые принципы

  • Ошибка в аргументах — это exit 1
  • Сообщения об ошибках пишем в stderr (через >&2)
  • Проверки делаем маленькими функциями, чтобы:
  • 1. было проще читать 2. было проще тестировать (запуская main.sh с разными аргументами) 3. было проще расширять, не ломая остальной код

    Код validate.sh

    Что здесь важно:

  • _die сразу завершает выполнение, поэтому после ошибки генерация не начнётся
  • _validate_file_pattern гарантирует “ровно одна точка” и ограничения длины
  • _parse_size_kb приводит 3kb к числу 3 и проверяет лимит <= 100
  • Если ты используешь set -u в main.sh, глобальную переменную FILE_SIZE_KB важно инициализировать именно в validate_args, иначе доступ к ней до валидации станет ошибкой.

    Как подключить валидацию в main.sh

    Точка входа должна быть тонкой: валидируем → инициализируем лог → запускаем генерацию.

    ``bash #!/usr/bin/env bash set -euo pipefail

    SCRIPT_DIR="(dirname "SCRIPT_DIR/lib/validate.sh" source "SCRIPT_DIR/lib/generator.sh"

    main() { validate_args "@" }

    main "path" ]] — правильная проверка

  • Разрешать лишние символы в “буквенных” параметрах
  • 1. часто забывают отсеять цифры, дефисы или кириллицу 2. безопасный минимум — ^[A-Za-z]+$
  • Не проверять “ровно одну точку” в параметре 5
  • 1. строка a.b.c должна считаться ошибкой
  • Неверно парсить kb
  • 1. 3KB, 3k, 3 kb по ТЗ не подходят 2. проще принять только строгий формат число + kb

    Что дальше по курсу

    После того как валидация готова и подключена, можно реализовывать генерацию имён и создание файлов, не боясь “грязного ввода”. В следующих шагах тебе понадобятся:

  • генерация базовой части имён с сохранением порядка букв и минимумом 4 символа
  • создание файлов заданного размера (обычно через truncate)
  • контроль свободного места через df
  • Полезные справки (понадобятся в следующих статьях):

  • truncate — GNU Coreutils
  • df — GNU Coreutils
  • date — GNU Coreutils
  • 3. Генерация имен папок: буквы, порядок, минимум 4 символа и суффикс DDMMYY

    Генерация имен папок: буквы, порядок, минимум 4 символа и суффикс DDMMYY

    Связь с предыдущими шагами

    В прошлых статьях ты:

  • зафиксировал требования и структуру src/01 с декомпозицией;
  • реализовал строгую валидацию 6 параметров в lib/validate.sh.
  • Теперь можно безопасно перейти к генерации имён папок: входные данные уже гарантированно имеют нужный формат (только английские буквы, длины в лимитах). Эта статья закрывает одну из самых “тонких” частей ТЗ: как построить строку имени из заданных букв так, чтобы соблюсти порядок, использовать каждую букву хотя бы раз, и обеспечить минимум 4 символа, а затем добавить суффикс даты _DDMMYY.

    Что именно требуется от имени папки

    Из ТЗ для папок следует набор правил:

  • Базовая часть имени (до даты) должна:
  • 1. состоять только из букв, заданных параметром 3; 2. использовать каждую из этих букв хотя бы один раз; 3. сохранять порядок букв, заданный во входном параметре; 4. иметь длину не менее 4 символов.
  • К базовой части добавляется суффикс даты запуска скрипта в формате DDMMYY через подчёркивание: _<дата>.
  • Пример из ТЗ: aaaz_021121.

    Как трактовать “сохранять порядок букв” без спорных случаев

    Фраза “если заданы символы az, то не может быть обратной записи zaaa” часто приводит к неоднозначной трактовке.

    Чтобы гарантированно пройти ревью, выберем самую безопасную интерпретацию: строим базовое имя как последовательность блоков в порядке исходных букв.

    То есть для az имя имеет вид:

  • сначала некоторое количество a (минимум 1),
  • затем некоторое количество z (минимум 1).
  • Тогда:

  • aaaz допустимо;
  • azzz допустимо;
  • zaaa невозможно получить этим способом, значит автоматически запрещено.
  • Эта стратегия проста, предсказуема и легко проверяется.

    !Визуальная схема показывает, как из набора букв формируется имя блоками и как добавляется суффикс даты.

    Алгоритм генерации базовой части имени

    Вход:

  • letters — строка букв (например, az или abc), гарантированно прошедшая валидацию;
  • min_len — минимум длины базовой части (по ТЗ это 4).
  • Выход:

  • строка, которая:
  • - содержит только буквы из letters; - содержит каждую букву из letters хотя бы один раз; - расположена блоками в заданном порядке; - имеет длину не меньше min_len.

    Простая и надёжная стратегия “добавляем в конец последний блок”

  • Собрать базовую часть как конкатенацию всех букв ровно по одному разу в исходном порядке.
  • 1. Для az получим az. 2. Для abc получим abc.
  • Если длина меньше min_len, добивать строку, добавляя последнюю букву столько раз, сколько нужно.
  • 1. Для az: azzz (длина 4). 2. Для abc: abcc (длина 4).

    Почему это соответствует правилам:

  • Каждая буква встречается хотя бы один раз, потому что мы добавили все буквы по одному.
  • Порядок не нарушается, потому что мы никогда не вставляем символы “внутрь” и не перемешиваем блоки: добавляем только в конец, увеличивая последний блок.
  • Минимум 4 достигается простым добиванием.
  • Примеры

    | letters | Шаг 1 (по одному разу) | Добивка до 4 | Базовая часть | |---|---|---|---| | a | a | aaa | aaaa | | az | az | zz | azzz | | abc | abc | c | abcc | | abcdefg | abcdefg | не нужна | abcdefg |

    Обрати внимание: для az получится azzz, а не aaaz. Оба варианта обычно принимаются, потому что они:

  • используют только разрешённые буквы,
  • включают a и z,
  • сохраняют порядок (нет z до a).
  • Если тебе принципиально получать именно “как в примере” (aaaz), можно добивать первый блок (букву a), но тогда тоже нельзя трогать блоки внутри строки. Важно не “переставлять” буквы местами.

    Суффикс даты _DDMMYY

    Суффикс должен быть одинаковым для всего запуска скрипта, поэтому дату лучше вычислить один раз в main.sh или внутри generator.sh и передавать в функции генерации.

    Команда:

  • date +%d%m%y
  • Это стандартная форма для получения DDMMYY. См. документацию GNU Coreutils: date — GNU Coreutils.

    Итоговое имя папки:

  • {date}
  • Пример:

  • azzz_060226
  • Реализация модуля src/01/lib/naming.sh

    Ниже минимальный и удобный модуль, который отвечает только за генерацию имён. Он не создаёт директории и не пишет лог, это важно для декомпозиции.

    Почему здесь всё ещё есть проверки

    Хотя входные параметры валидируются в validate.sh, модуль имён лучше делать “защищённым”:

  • Его могут вызвать из другого места, и ошибки должны быть понятными.
  • Это упрощает отладку: ты сразу видишь, где и почему сломалось.
  • Важно: эти проверки не дублируют всю валидацию ТЗ, они только защищают функции от явно невозможных состояний (пустые строки, нечисловой min_len).

    Как подключить naming.sh в main.sh или generator.sh

    Обычно модуль имён используется генератором.

    Пример подключения в src/01/main.sh:

    Частые ошибки в реализации имён

  • Нарушение порядка букв из параметра.
  • 1. Если ты вставляешь символы “в начало” или “в середину”, легко случайно получить zaaa при входе az.
  • Игнорирование требования “каждая буква хотя бы раз”.
  • 1. Если просто повторять первую букву до длины 4, то при az получится aaaa, что нарушает правило.
  • Генерация даты заново для каждого объекта.
  • 1. Теоретически в долгом запуске дата может смениться в полночь, и ты получишь разные суффиксы. 2. По духу ТЗ дата должна соответствовать запуску, поэтому вычисляй её один раз.

    Что дальше

    После того как имена папок научились корректно строиться, следующий шаг обычно такой:

  • генерация имён файлов по тем же правилам, но с отдельной генерацией расширения;
  • контроль уникальности имён (что делать, если путь уже существует);
  • создание файлов заданного размера и проверка свободного места через df.
  • Полезно прогонять код через статический анализатор: ShellCheck.

    4. Генерация имен файлов и расширений: ограничения 7/3, порядок и обязательность букв

    Генерация имен файлов и расширений: ограничения 7/3, порядок и обязательность букв

    Связь с предыдущими статьями

    В прошлых шагах ты:

  • зафиксировал требования, структуру src/01 и декомпозицию на модули;
  • реализовал строгую валидацию 6 параметров в lib/validate.sh;
  • сделал генерацию имён папок в lib/naming.sh: буквы по параметру, порядок, минимум 4 символа и суффикс даты _DDMMYY.
  • Теперь нужно научиться генерировать имена файлов и расширения по параметру 5 (name.ext), соблюдая ограничения 7/3, порядок букв и требование “каждая буква используется хотя бы один раз”.

    !Поясняет, из каких частей строится имя файла и где применяются правила

    Что требует ТЗ от имени файла

    Параметр 5 передаётся как строка вида az.az и валидацией (в предыдущей статье) уже гарантируется:

  • слева от точки только английские буквы, длина не более 7;
  • справа от точки только английские буквы, длина не более 3;
  • точка ровно одна.
  • Дальше вступают правила построения имён:

  • имя файла до даты должно состоять только из букв из части до точки;
  • каждая буква из части до точки должна встретиться хотя бы один раз;
  • порядок букв должен сохраняться (для az нельзя получить ситуацию, где z идёт раньше a в этой “буквенной части”);
  • длина “буквенной части имени” должна быть минимум 4 символа;
  • затем добавляется _DDMMYY (дата запуска) как в именах папок;
  • расширение состоит только из букв из части после точки, использует каждую букву хотя бы один раз и сохраняет порядок.
  • Важно аккуратно трактовать правило “минимум 4 символа”: в ТЗ речь идёт про “эту часть имени” перед датой. Расширение при этом ограничено 3 символами, поэтому применять к нему минимум 4 невозможно. Значит:

  • минимум 4 применяем к имени файла до _DDMMYY;
  • к расширению применяем только правило “каждая буква хотя бы один раз” и “порядок”.
  • Безопасная трактовка “сохранять порядок” для файлов

    Чтобы избежать спорных случаев на ревью, используем ту же безопасную стратегию, что и для папок:

  • строим строку блоками в исходном порядке;
  • не вставляем буквы “в начало” и не перемешиваем;
  • если нужно увеличить длину, добавляем только последнюю букву, увеличивая последний блок.
  • Пример для az:

  • допустимо: azzz;
  • недопустимо: zaaa.
  • Алгоритм генерации имени файла

    Ниже алгоритм, который удобно реализовать в lib/naming.sh.

    Генерация базовой части имени файла

    Вход:

  • name_letters из параметра 5 (до точки);
  • минимальная длина 4.
  • Шаги:

  • Берём базу как base = name_letters.
  • Если длина меньше 4, добавляем в конец последнюю букву из name_letters, пока длина не станет 4.
  • Свойства:

  • каждая буква из name_letters использована хотя бы раз, потому что base начинается с name_letters;
  • порядок букв сохранён, потому что добавляем только последнюю букву в конец;
  • длина минимум 4 гарантирована.
  • Добавление суффикса даты

    Дата должна быть общей для всего запуска, поэтому её нужно вычислить один раз (например, в generator.sh) и передавать в функции генерации.

  • формат: DDMMYY;
  • имя: {run_date}.
  • Генерация расширения

    Вход:

  • ext_letters из параметра 5 (после точки).
  • Шаги:

  • Берём расширение как ext = ext_letters.
  • Ничего не добиваем до 4, потому что расширение ограничено 3 символами.
  • Свойства:

  • каждая буква использована хотя бы раз;
  • порядок сохранён;
  • длина не превышает 3 (это обеспечено валидацией).
  • Сборка итогового имени файла

    Итог:

  • {run_date}.1" >&2
  • exit 1 }

    get_run_date_ddmmyy() { date +%d%m%y }

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

    extra_last добавляет дополнительное число повторов последней буквы (для уникальности).

    build_base_name() { local letters="2" local extra_last="letters" ]] || _die_naming "letters is empty" [[ " ]] || _die_naming "min_len must be positive integer" [[ " ]] || _die_naming "extra_last must be non-negative integer"

    local base="{letters: -1}"

    while (( last_char" done

    local i for ((i=0; i<extra_last; i++)); do base+="base" }

    Расширение: используем все буквы хотя бы раз и сохраняем порядок.

    Тут не применяем min_len=4, потому что расширение ограничено 3.

    build_extension() { local ext_letters="ext_letters" ]] || _die_naming "ext_letters is empty"

    echo "1" local ext_letters="3" local extra_last="run_date" ]] || _die_naming "run_date is empty"

    local base base="name_letters" 4 "(build_extension "{base}_{ext}" }

    Частые ошибки при генерации файловых имён

  • Делать имя файла короче 4 символов до даты, например az_060226.az.
  • Менять порядок букв, например пытаться “рандомизировать” и получить zaaa_... при az.
  • Пытаться добивать расширение до 4 символов, нарушая ограничение “не более 3”.
  • Генерировать дату на каждый файл, а не один раз на запуск.
  • Что дальше по курсу

    После того как имена файлов и расширения генерируются корректно, следующий практический шаг:

  • создание файлов нужного размера через truncate или dd;
  • проверка свободного места на / через df` и остановка при пороге 1 GiB;
  • полноценное логирование созданных файлов и папок.
  • 5. Создание дерева каталогов и файлов заданного размера (до 100 KB)

    Создание дерева каталогов и файлов заданного размера (до 100 KB)

    Связь с предыдущими шагами

    Ранее ты сделал два ключевых слоя:

  • Валидация входных параметров в lib/validate.sh (включая парсинг размера Nkb в число FILE_SIZE_KB и проверку лимита <= 100).
  • Генерацию имён директорий и файлов в lib/naming.sh с соблюдением порядка букв, обязательного использования всех букв и суффикса даты _DDMMYY.
  • Теперь нужно реализовать слой, который действительно создаёт директории и файлы в файловой системе.

    Эта статья отвечает на вопрос: как корректно создать дерево N папок и M файлов заданного размера, не смешивая логику именования, генерации и операций с ФС.

    !Диаграмма модулей и порядка вызовов при создании директорий и файлов

    Что именно нужно сделать на этом этапе

    По ТЗ в директории из параметра 1 нужно:

  • создать указанное количество папок (параметр 2);
  • в каждой папке создать указанное количество файлов (параметр 4);
  • каждый файл должен иметь размер из параметра 6 в килобайтах и не превышать 100KB.
  • Важно: требования про остановку при свободном месте 1GB на / и полноценное логирование мы интегрируем на уровне генератора и отдельного модуля, но операции создания (mkdir и создание файла заданного размера) должны быть вынесены в lib/fs.sh.

    Почему операции с ФС стоит вынести в отдельный модуль

    Практически это решает сразу несколько задач:

  • naming.sh остаётся чистым генератором строк и не зависит от диска.
  • generator.sh управляет циклами и уникальностью имён, но не знает деталей, как именно создать файл нужного размера.
  • fs.sh изолирует команды mkdir, dd, truncate, stat и обработку ошибок.
  • Так декомпозиция выглядит убедительно на ревью и проще отлаживается.

    Размер файла: как гарантировать нужные килобайты

    Параметр 6 задаётся в килобайтах. В Linux обычно под килобайтом в утилитах подразумевается блок байта.

    Есть два частых способа создать файл нужного размера:

  • truncate -s 10K file создаёт файл с заданным размером очень быстро.
  • dd if=/dev/zero of=file bs=1K count=10 записывает реальные байты и создаёт обычный, не разрежённый файл.
  • Что выбрать для School21

    Чтобы избежать спорных ситуаций, безопаснее использовать dd, потому что он реально заполняет файл и размер будет одинаково интерпретироваться и как логический размер, и как занятое место.

    Ссылки:

  • dd — GNU Coreutils
  • truncate — GNU Coreutils
  • Реализация src/01/lib/fs.sh

    Модуль должен дать генератору два простых инструмента:

  • создать директорию;
  • создать файл нужного размера в KB.
  • Создай файл src/01/lib/fs.sh.

    Что здесь важно

  • run_date вычисляется один раз на запуск.
  • Для файла уникальность достигается через extra, который увеличивает повтор последней буквы в базовой части имени и не нарушает порядок.
  • create_file_kb получает число килобайт, а не строку 3kb. Разбор уже сделал validate.sh.
  • Куда подключать модули

    В src/01/main.sh у тебя должны быть source всех зависимостей, которые нужны генератору.

    ``bash #!/usr/bin/env bash set -euo pipefail

    SCRIPT_DIR="(dirname "SCRIPT_DIR/lib/validate.sh" source "SCRIPT_DIR/lib/fs.sh" source "SCRIPT_DIR/lib/log.sh"

    main() { validate_args "@" }

    main "3 \cdot 1024 = 3072\le 1$ GiB на / (обычно проверяется через df -k /);

  • логирование каждой созданной директории и каждого файла.
  • Дальше ты встроишь эти требования, не ломая текущую декомпозицию: проверка места будет в fs.sh или отдельном модуле, а логирование останется в log.sh` и будет вызываться после успешного создания.

    6. Контроль свободного места: остановка при остатке 1 GB на разделе /

    Контроль свободного места: остановка при остатке 1 GB на разделе /

    Как это связано с предыдущими шагами

    Ранее ты уже собрал основу решения:

  • в lib/validate.sh проверяешь 6 параметров и парсишь размер файлов в FILE_SIZE_KB
  • в lib/naming.sh генерируешь корректные имена папок и файлов
  • в lib/fs.sh умеешь создавать директории и файлы заданного размера
  • в lib/generator.sh управляешь циклами создания дерева
  • Теперь добавляем обязательное требование ТЗ: остановить работу скрипта, если на файловой системе раздела / останется 1 GB свободного места.

    !Схема, где именно в генерации выполняется проверка свободного места

    Что именно требует ТЗ и как это корректно трактовать

    ТЗ говорит: остановить работу, если в файловой системе (в разделе /) останется 1 Гб свободного места.

    Практическая трактовка для надежной реализации:

  • проверяем свободное место на разделе /, а не в директории из параметра 1
  • проверяем перед созданием каждого файла, потому что именно файлы расходуют место наиболее заметно
  • если свободного места стало меньше либо равно порогу (1 GB) — прекращаем создание и завершаем скрипт
  • Важно: для стабильности проверку лучше делать до операции записи, а не после.

    Как получить свободное место на разделе /

    Самый простой и переносимый способ в Linux — df.

    Команда:

  • ключ -k заставляет выводить значения в килобайтах
  • нас интересует колонка Available (доступно)
  • Чтобы получить число доступных килобайт, удобно взять вторую строку и четвертую колонку:

    Почему awk:

  • он стабильно достает нужное поле из табличного вывода
  • не требует сложного парсинга строк в Bash
  • Документация:

  • df — GNU Coreutils
  • awk — gawk manual
  • Порог 1 GB: во что сравнивать

    df -k возвращает доступное место в килобайтах. Тогда порог 1 GB нужно тоже хранить в килобайтах.

    В учебных задачах по Linux обычно подразумевается двоичная интерпретация:

  • 1 KB = 1024 bytes
  • 1 GB (точнее 1 GiB) = 1024 MB = 1024 * 1024 KB
  • Значит порог в килобайтах:

  • 1048576 KB
  • Чтобы не размазывать магические числа по коду, вынеси это в константу.

    Добавляем константу в conf/constants.sh

    Создай файл src/01/conf/constants.sh (если его еще нет) и положи туда порог.

    Реализация проверки в lib/fs.sh

    Логика работы с файловой системой должна быть сосредоточена в lib/fs.sh, поэтому добавим туда:

  • функцию получения свободного места
  • функцию, которая прерывает работу при достижении порога
  • Дополни src/01/lib/fs.sh.

    ``bash #!/usr/bin/env bash

    _fs_die() { echo "Error: (df -k / | awk 'NR==2 {print free_kb" =~ ^[0-9]+free_kb'" echo "{MIN_FREE_KB_ROOT:?MIN_FREE_KB_ROOT is not set. Did you source conf/constants.sh?}"

    local free_kb free_kb="{free_kb}KB, threshold: (cd "{BASH_SOURCE[0]}")" && pwd)"

    source "SCRIPT_DIR/lib/validate.sh" source "SCRIPT_DIR/lib/fs.sh" source "SCRIPT_DIR/lib/generator.sh"

    main() { validate_args "@" }

    main "(make_file_name "ext_letters" "extra")" file_path="file_name"

    [[ ! -e "((extra + 1)) done

    create_file_kb "FILE_SIZE_KB"

    # log_file "4}', чем разбирать пробелы вручную

  • Проверять место после создания файла
  • - можно успеть перейти порог, особенно если размер файла большой и места мало
  • Не делать проверку числа после df
  • - защита от неожиданного вывода экономит много времени при отладке

    Что дальше

    После добавления контроля свободного места у тебя закрывается еще одно обязательное требование ТЗ. Следующий шаг курса логически такой:

  • завершить модуль log.sh и встроить логирование так, чтобы в лог попадали все созданные файлы и директории, а также было понятно, почему генерация остановилась.
  • 7. Логирование: полный путь, дата создания, размер и итоговая самопроверка

    Логирование: полный путь, дата создания, размер и итоговая самопроверка

    Зачем логирование обязательно в этом проекте

    По ТЗ генератор должен записывать лог-файл со сведениями о всех созданных папках и файлах:

  • полный путь;
  • дата создания;
  • размер для файлов.
  • Лог — это не “доп. фича”, а часть результата, по которому удобно проверять корректность работы скрипта и разбирать спорные ситуации на ревью.

    !Диаграмма порядка вызовов и точки логирования

    Что считать “датой создания” в Bash-скрипте

    В Linux “реальная дата создания” файла (birth time) не гарантирована на всех файловых системах и иногда недоступна через stat. Поэтому в учебном проекте самый надёжный подход:

  • Считать датой создания время, когда скрипт успешно создал объект.
  • Писать в лог строку сразу после mkdir или dd.
  • Время берём через date.

    Справка:

  • date — GNU Coreutils Manual
  • Формат лог-файла

    Сделай формат лога простым для чтения и парсинга.

    Рекомендуемый формат строки:

    | Тип записи | Поля | |---|---| | Папка | DIR | <полный_путь> | <дата> | | Файл | FILE | <полный_путь> | <дата> | <размер> |

    Где:

  • полный путь — абсолютный путь до созданного объекта;
  • дата — строка вида YYYY-MM-DD HH:MM:SS;
  • размер — удобно писать в человекочитаемом виде как <N>KB, строго по входному параметру, и при желании дополнительно логировать байты.
  • Реализация src/01/lib/log.sh

    Модуль логирования должен:

  • создать каталог для логов;
  • создать новый лог-файл на каждый запуск;
  • предоставлять функции log_dir и log_file.
  • Создай файл src/01/lib/log.sh.

    Справка:

  • stat — GNU Coreutils Manual
  • Главное правило: логировать только после успешного создания

    Чтобы лог не “врал”, порядок должен быть таким:

  • Создали директорию mkdir.
  • Только после успеха сделали log_dir.
  • Перед созданием файла проверили свободное место.
  • Создали файл dd.
  • Только после успеха сделали log_file.
  • Это особенно важно при условии остановки по свободному месту: скрипт может завершиться посреди процесса, а лог должен отражать только реально созданные сущности.

    Встраивание логирования в generator.sh

    Ниже показан фрагмент, как корректно встроить логирование в текущую архитектуру.

    Требования к окружению:

  • FILE_SIZE_KB вычислен в validate_args.
  • MIN_FREE_KB_ROOT задан в conf/constants.sh.
  • init_log вызван в main.sh до run_generator.
  • Пример src/01/lib/generator.sh с ключевыми местами.

    Самопроверка: что лог есть и в нём правильные поля

  • Найди последний лог:
  • Посмотри первые строки:
  • Самопроверка: что лог содержит полные пути

    Проверь, что строки лога начинаются с / внутри поля пути:

    ``bash grep '^FILE |' "src/01/logs/dir_path/file_name".

    Частые ошибки в логировании

  • Логировать до создания сущности.
  • Логировать относительные пути вместо абсолютных.
  • Пытаться брать “дату создания” через нестабильные поля stat.
  • Писать лог в произвольное место вне src/01.
  • Перезаписывать один и тот же лог на каждый запуск, теряя историю.
  • Что дальше

    После этой статьи у тебя закрыты последние обязательные требования ТЗ для Part 1:

  • корректные имена;
  • создание файлов нужного размера;
  • остановка по свободному месту на /;
  • логирование всех созданных объектов.
  • Остаётся финально привести код к аккуратному виду, прогнать через ShellCheck и подготовиться к ревью.