Bash-скрипт для коллеги: аргументы, пути и понятный результат
Команда для подсчёта файлов прекрасно работает в терминале автора. Коллега запускает её из другого каталога и получает ноль, хотя файлы на месте. В следующей версии появляется вопрос «какой каталог ты имел в виду?», а затем скрипт нужно вызвать из CI, где спросить уже не у кого. Сам вычислительный шаг остаётся простым; сложность возникает на границе между программой и её пользователем.
Эту границу полезно определить до добавления новых функций. Что передаётся явно, относительно чего разрешаются пути, где находится результат и как вызывающая программа отличит ошибку от пустых данных? Когда ответы устойчивы, скрипт можно использовать руками, из другого скрипта и из планировщика без отдельной версии для каждого случая.
Сделаем маленький законченный инструмент
Скрипт ниже считает обычные файлы непосредственно в указанном каталоге. Вложенные каталоги он не обходит. Имя каталога — обязательный аргумент, относительный путь отсчитывается от текущего каталога вызывающего процесса. Результат — одно целое число в stdout; ошибки и инструкция идут в stderr.
#!/usr/bin/env bash
if [[ $# -ne 1 ]]; then
printf 'Usage: %s DIRECTORY\n' "$0" >&2
exit 2
fi
if [[ ! -d "$1" || ! -r "$1" || ! -x "$1" ]]; then
printf 'Directory is unavailable: %s\n' "$1" >&2
exit 1
fi
shopt -s nullglob dotglob
count=0
for entry in "$1"/*; do
if [[ -f "$entry" && ! -L "$entry" ]]; then
count=$((count + 1))
fi
done
printf '%s\n' "$count"
Сохраните код как count-files.sh. Bash нужен явно: конструкции [[ ... ]] и shopt не относятся к переносимому POSIX sh. Вызывать файл можно через bash count-files.sh DIRECTORY; для непосредственного запуска дополнительно нужны разрешение исполнения и доступный интерпретатор.
Параметры nullglob и dotglob определяют смысл пустого каталога и скрытых имён. Без nullglob шаблон без совпадений остаётся буквальной строкой. С dotglob учитываются файлы, начинающиеся с точки. Символические ссылки исключены отдельным условием, чтобы результат соответствовал сформулированному правилу: считать сами обычные файлы верхнего уровня.
Отделите пользовательскую ошибку от нулевого результата
Пустой доступный каталог даёт число 0 и успешный код завершения. Отсутствующий аргумент даёт код 2, недоступный каталог — код 1. Эти значения выбраны для интерфейса этого скрипта и описываются рядом с ним. Получатель может сохранить stdout в переменную и сначала проверить статус, вместо поиска слова «ошибка» среди сообщений.
Не добавляйте в stdout приветствие и пояснение перед числом: это сломает потребителя, ожидающего машинный результат. Подробное сообщение можно вывести в stderr. Для инструмента с несколькими режимами полезен --help, но для единственного обязательного аргумента достаточно короткой инструкции при неверном вызове. Новые параметры стоит добавлять под реальную задачу.
Договоритесь о путях
В этом интерфейсе каталог передаёт пользователь. Скрипту не нужно переходить в каталог своего файла и тем самым менять смысл относительного аргумента. Если инструменту нужен ресурс из собственного репозитория, вычислите путь к нему отдельно. Не смешивайте пользовательский путь и путь поставляемого шаблона под одним неявным текущим каталогом.
Проверьте запуск с абсолютным путём, относительным путём и именем с пробелом. Сама поддержка сложных имён — лишь один элемент интерфейса. Важнее, что одинаковый вызов должен означать одинаковую операцию, а отсутствующий доступ не должен выглядеть как полезный нулевой результат.
Каталог может измениться во время обхода, поэтому такой подсчёт подходит для локальной проверки и отчёта по относительно спокойному набору файлов. Для строгого снимка изменяемого хранилища сначала нужен механизм снимка или остановки записи. Предварительная проверка прав также не гарантирует, что права не поменяются через мгновение после неё.
Проверьте контракт, затем расширяйте
Создайте временный каталог: два обычных файла, скрытый файл, вложенный каталог и ссылку. Ожидаемый результат — три, поскольку содержимое вложенного каталога и ссылка исключены. Затем проверьте пустой каталог и вызов без аргумента. Здесь тестируется смысл интерфейса, а не только отсутствие синтаксической ошибки Bash.
Когда появится запрос на рекурсивный подсчёт, оформите его явно и дополните примеры. Иначе маленькое изменение цикла незаметно изменит число для существующих пользователей. Храните сам скрипт, договорённости и проверки в Git одним изменением: так коллега увидит, почему поменялось поведение.
Для дальнейшего маршрута сравните содержание обучения Linux, Bash и Git для разработчика: выполнение команд и сохранение изменений скрипта дополняют друг друга.
Если хотите уверенно писать такие инструменты, начните с курса «Bash Linux + Git». В программе есть переменные и аргументы, условия, циклы, функции и массивы, а также авторские задачи на написание скриптов. Затем вы разберёте ветки, историю и совместную работу в Git. Следующий шаг после статьи — перестать собирать каждый сценарий из случайных команд и разобраться, как оболочка передаёт параметры и выполняет команды. Откройте программу курса и закрепите эти навыки на задачах.
Продолжить на практике
- Bash Linux + Git: от новичка до уверенного пользователя ↗
Освоим два важнейших инструмента разработчика: применение bash-скриптов в практических задачах и git в его самых разных проявлениях.
