Проверка наличия файлов и каталогов в Bash
Проверяйте наличие файлов и каталогов в Bash — не полагайтесь на окружение. Используйте встроенные тесты (-f, -d, -r, -w и др.) в [[ ]] или в test, сочетайте логические операторы, обрабатывайте ошибки и записывайте действия. Это повышает надёжность скриптов и уменьшает риск повреждения данных.

Быстрые ссылки
Не делайте предположений
Диапазон тестов
Использование тестов в скриптах
Тестируйте, не предполагая
Не делайте предположений
Когда вы пишете сценарий (скрипт), нельзя полагаться на то, какие файлы или каталоги уже присутствуют в системе. Это особенно важно, если скрипт будет запускаться на многих разных компьютерах: однажды он попадёт в среду, где ваши допущения неверны, и результат может быть неприятным — от простого сообщения об ошибке до потери данных.
Вся полезная информация на компьютере хранится в файлах и каталогах. Скрипты могут читать, писать, переименовывать, удалять и перемещать объекты в файловой системе — то же, что вы делаете вручную в терминале. Человеку проще посмотреть содержимое каталога и понять, что необходимо, тогда как скрипт этого не умеет, если вы явно не проверите условия.
Bash предоставляет набор тестов для проверки существования файлов и каталогов и их атрибутов. Включив их в скрипт, вы получите более устойчивое поведение и тонкий контроль над тем, какие действия допустимы.
Определение: двойные квадратные скобки [[ ]] — рекомендуемая конструкция для условных выражений в Bash; она расширяет возможности по сравнению с классической командой test или одинарными [ ].
Диапазон тестов
Ниже — список стандартных файловых тестов, доступных в Bash (внутри [[ ]]). Коротко: предназначение теста — вернуть истину (true), если проверка проходит.
- -b: истина, если файл — блочное специальное устройство (block device).
- -c: истина, если файл — символьное специальное устройство (character device).
- -d: истина, если объект — каталог.
- -e: истина, если файл/каталог существует.
- -f: истина, если файл существует и является обычным файлом (не каталог и не устройство).
- -g: истина, если установлен бит setgid (chmod g+s).
- -h: истина, если объект — символьная ссылка (symlink).
- -L: истина, если объект — символьная ссылка (альтернативный флаг к -h).
- -k: истина, если установлен sticky‑бит (chmod +t).
- -p: истина, если файл — именованный канал (named pipe, FIFO).
- -r: истина, если файл доступен для чтения текущим пользователем.
- -s: истина, если файл существует и его размер больше нуля (непустой файл).
- -S: истина, если файл — unix-сокет.
- -t: истина, если файловый дескриптор открыт в терминале (обычно используется с дескриптором).
- -u: истина, если установлен бит setuid (chmod u+s).
- -w: истина, если файл доступен для записи текущим пользователем.
- -x: истина, если файл исполняем (execute) текущим пользователем.
- -O: истина, если файл принадлежит текущему пользователю.
- -G: истина, если файл принадлежит группе текущего пользователя.
- -N: истина, если файл был изменён после последнего чтения (использует временные метки).
Логические операторы:
- ! : логическое НЕ.
- && : логическое И.
- || : логическое ИЛИ.
Примечание: флаг -a не рекомендуется (deprecated) — вместо него используйте -e для проверки существования.
Использование тестов в скриптах
Ниже — проверенные шаблоны и примеры, которые можно использовать напрямую. Скопируйте код в файл, например script1.sh, и сделайте его исполняемым: chmod +x script1.sh.
Пример 1 — базовая проверка наличия обычного файла:
#!/bin/bash
if [[ -f $1 ]]
then
echo "Файл $1 существует."
else
echo "Файл $1 не найден."
fiЗапуск:
./script1.sh test-file.txtЕсли файл существует, скрипт выведет сообщение и продолжит работу; если нет — вы можете принять решение: создать файл, завершить работу с ошибкой, или восстановить из резервной копии.
Пример 2 — проверка устройств и обычных файлов (script2.sh):
#!/bin/bash
if [[ -f $1 ]]
then
echo "Файл $1 существует."
else
echo "Файл $1 отсутствует или не является обычным файлом."
fi
if [[ -c $1 ]]
then
echo "Файл $1 — символьное устройство (character device)."
else
echo "Файл $1 отсутствует или не является специальным символьным файлом."
fiПример 3 — объединение проверок с логическим ИЛИ (script3.sh):
#!/bin/bash
if [[ -f $1 ]]
then
echo "Файл $1 существует."
else
echo "Файл $1 отсутствует или не является обычным файлом."
fi
if [[ -c $1 || -b $1 ]]
then
echo "Файл $1 — символьное или блочное устройство."
else
echo "Файл $1 отсутствует или не является устройством."
fiПример 4 — вложенные проверки для различения типов устройств (script4.sh):
#!/bin/bash
if [[ -f $1 ]]
then
echo "Файл $1 существует."
else
echo "Файл $1 отсутствует или не является обычным файлом."
fi
if [[ -c $1 ]]
then
echo "Файл $1 — символьное устройство."
else
if [[ -b $1 ]]
then
echo "Файл $1 — блочное устройство."
else
echo "Файл $1 отсутствует или не является устройством."
fi
fiПример 5 — проверка наличия и прав на чтение/запись (script5.sh):
#!/bin/bash
if [[ -f $1 && -r $1 && -w $1 ]]
then
echo "Файл $1 существует и доступны чтение/запись."
else
echo "Файл $1 отсутствует, не является обычным файлом или недостаточно прав."
fiПример 6 — проверка каталога и создание при отсутствии (часть резервного сценария, script6.sh):
#!/bin/bash
if [[ ! -d $1 ]]
then
echo "Создаём каталог резервной копии: $1"
mkdir -p "$1"
if [[ ! $? -eq 0 ]]
then
echo "Не удалось создать каталог резервной копии: $1"
exit 1
fi
else
echo "Каталог резервной копии существует."
fi
# continue with file backup
echo "Выполняется резервное копирование в: $1"В этом примере мы используем mkdir -p для безопасного создания вложенных каталогов и проверяем код возврата команды через $?. В продакшн-скриптах лучше заменить проверку по $? на немедленную обработку ошибок (set -e или явная проверка команд).



Когда один тест не хватает
Иногда одного теста недостаточно, потому что объект может соответствовать нескольким критериям или вы хотите отфильтровать ложные срабатывания.
Примеры ситуаций:
- /dev/random и другие устройства: -f вернёт false, потому что это не «обычный» файл; используйте -c или -b.
- Символьная ссылка может вести на несуществующий файл: [[ -h link && -e link ]] покажет, что ссылка есть, а цель существует.
- Если вам важен размер файла, используйте -s; для пустых файлов -s даст false.
В этих случаях комбинируйте тесты и обрабатывайте все ветки логики.
Лучшие практики и методология
Мини‑методология для устойчивых скриптов взаимодействия с файловой системой:
- Валидация аргументов: проверьте, что обязательные параметры переданы.
- Предварительные тесты: существование, тип (файл/каталог/устройство), права доступа.
- Безопасные операции: используйте mkdir -p, cp -a, mv –backup=numbered по необходимости.
- Обработка ошибок: проверяйте коды возврата команд и логируйте причины отказа.
- Возврат и завершение: при критической ошибке завершайте скрипт с ненулевым кодом.
- Идемпотентность: по возможности делайте операции так, чтобы повторный запуск не повредил данным.
Шаблон для сценария с детальной обработкой ошибок:
#!/bin/bash
set -o errexit
set -o nounset
set -o pipefail
TARGET="$1"
if [[ -z "$TARGET" ]]
then
echo "Ошибка: не указан путь." >&2
exit 2
fi
if [[ ! -e "$TARGET" ]]
then
echo "Цель $TARGET не существует. Выполняется создание..."
mkdir -p "$(dirname "$TARGET")"
touch "$TARGET"
fi
# Дальнейшие операции с $TARGET...Примечание: директивы set -o errexit и set -o pipefail помогают поймать ошибки, но требуют внимательной логики при ожидаемых ошибках.
Альтернативные инструменты и подходы
- Команда test и одинарные скобки [ ] работают во всех POSIX-совместимых оболочках, но имеют ограничения с операторами && и || внутри.
- stat — даёт подробную информацию о файле (временные метки, тип, права). Полезно, если вы проверяете атрибуты, которых нет в простых тестах.
- find — можно использовать для массовых выборок файлов с нужными критериями (по типу, правам, размеру, времени изменения).
- Проверки на уровне приложений: иногда лучше поручить проверку файлов библиотеке/программе на более высоком уровне.
Ментальные модели и эвристики
- Принцип минимального воздействия: выполняйте только те операции, которые действительно нужны (не удаляйте, если можете архивировать).
- Правило трёх защит: валидируйте вход, проверяйте состояние, подтверждайте успешность операции.
- Идемпотентность: скрипт должен быть безопасен при повторном запуске.
Контрольные списки по ролям
Администратор/DevOps:
- Проверить, какие пользователи будут запускать скрипт.
- Настроить права на каталоги и файлы.
- Решить политику создания резервных копий перед изменением.
Разработчик:
- Добавить проверки аргументов и поясняющие сообщения об ошибках.
- Логировать критические операции.
- Писать тесты для сценарных случаев: файл отсутствует/нет прав/пустой файл.
SRE/QA:
- Создать набор тестовых сред с различными состояниями ФС.
- Автоматизировать прогон скриптов в CI с имитацией отсутствующих файлов и ошибок прав.
Критерии приёмки
Чтобы принять скрипт для производства, он должен:
- Обрабатывать все ожидаемые виды входных данных (существует/не существует, файл/каталог/устройство).
- Корректно возвращать коды завершения: 0 при успехе, >0 при ошибке.
- Логировать причины отказа и не терять данные без явного согласия.
- Быть идемпотентным или явно документировать побочные эффекты.
Примеры тест-кейсов (приёмочные)
- Ввод: существующий обычный файл. Ожидается: успешная проверка, операции выполнены.
- Ввод: отсутствующий файл. Ожидается: создание файла/ошибка, как задокументировано.
- Ввод: файл без прав записи. Ожидается: аккуратное сообщение об ошибке и код >0.
- Ввод: символьная ссылка на несуществующую цель. Ожидается: сообщение о разорванной ссылке.
Диаграмма принятия решения
flowchart TD A[Получен путь] –> B{Путь существует?} B – Нет –> C{Нужен ли файл/каталог?} C – Да –> D[Создать (mkdir/touch)] C – Нет –> E[Ошибка/выход] B – Да –> F{Это каталог?} F – Да –> G[Проверить права на запись] F – Нет –> H{Обычный файл?} H – Да –> I[Проверить чтение/запись/исполнение] H – Нет –> J[Определить тип устройства (c/b/S/…)] G –> K[Действие с каталогом] I –> K J –> K K –> L[Логирование/возврат кода]
Когда тесты могут подвести
- В средах с сетевыми файловыми системами (NFS) статусы могут кэшироваться — время отклика и видимость изменений зависит от настроек.
- Расширенные атрибуты (ACL) могут давать доступ, даже если базовые флаги не показывают его: проверяйте фактический доступ попыткой чтения/записи, если это критично.
- Race conditions: между проверкой и действием другой процесс может изменить файл. Чтобы избежать гонок, выполняйте атомарные операции (например, tmp-файлы и rename).
Совет: для операций, где критично, что состояние не изменилось между проверкой и использованием, применяйте атомарные приёмы: создавайте временный файл и делайте mv в целевой путь, используйте flock для блокировки ресурсов.
Шпаргалка (cheat sheet)
- Проверка существования: [[ -e path ]] или [[ -f path ]] (обычный файл).
- Проверка каталога: [[ -d path ]]
- Проверка исполняемости: [[ -x path ]]
- Проверка чтения/записи: [[ -r path ]], [[ -w path ]]
- Проверка пустоты: [[ -s path ]] (истина, если файл непустой)
- Создать каталог: mkdir -p path
- Создать файл: touch path
- Проверить код возврата последней команды: if [[ $? -ne 0 ]]; then … fi
Безопасность и приватность
- Не доверяйте значениям переменных окружения без проверки.
- Если скрипт работает с конфиденциальными файлами, ограничьте вывод в логи и права доступа на лог-файлы.
- Прежде чем копировать файлы в место с более широким доступом, убедитесь в необходимости и задокументируйте риски.
Краткое резюме
Тесты в Bash дают вам знание о текущем состоянии файловой системы. Используйте их системно: валидируйте вход, определяйте тип объекта, проверяйте права и обрабатывайте ошибки. Комбинация тестов, логики и безопасных операций делает скрипты надёжными и предсказуемыми.
Важное: проверка не заменяет атомарных операций и механизмов синхронизации — при риске гонок используйте временные файлы, rename и локи.
Спасибо за прочтение. Начните с малого: возьмите существующий скрипт и добавьте проверки -e/-d/-f там, где это критично.
Похожие материалы
ISERROR в Excel — находить и обрабатывать ошибки
Как исправить I/O device error в Windows
Открыть пакет приложения на macOS
Принудительное закрытие зависших программ в Windows
TikTok AI Greenscreen — как создавать фоны для видео