«Ошибка «Permission denied» при запуске скрипта — это не просто техническая преграда, а сторожевой пес операционной системы, который заставляет задуматься: что именно ты пытаешься исполнить и с какими правами».
Что происходит при запуске скрипта
Ввод ./script.sh в терминале запускает цепочку событий, которую многие представляют слишком упрощенно. Ядро Linux проверяет не только наличие бита исполнения. Последовательность выглядит так:
- Проверка бита исполнения (x). Оболочка запрашивает у ядра выполнение файла. Ядро в первую очередь смотрит на права доступа файла для текущего пользователя. Если бит исполнения отсутствует — процесс останавливается с ошибкой
Permission denied. - Анализ содержимого файла. Если права есть, ядро читает первые несколько байт файла. Оно ищет сигнатуру
#!(shebang). - Создание нового процесса. Найденный интерпретатор (например,
/bin/bash) запускается в отдельном процессе. Ему передается путь к скрипту как аргумент. Важно: скрипт сам по себе не является исполняемой программой — это текст, который читает интерпретатор. - Исполнение. Интерпретатор построчно читает и выполняет команды из файла в новом процессе.
Сбой на любом из этих шагов приводит к разным ошибкам: от явного Permission denied до неочевидного bad interpreter, если путь в shebang неверен.
Пять способов запуска и их последствия
Способ запуска определяет не только необходимость в правах, но и контекст выполнения — это критично для безопасности и поведения скрипта.
| Команда | Требует бит +x | Контекст выполнения | Влияние на текущую оболочку |
|---|---|---|---|
./script.sh |
Да | Новый процесс. Запускается интерпретатор из shebang. | Изменения переменных среды, рабочий каталог (cd), объявленные функции — всё остаётся внутри нового процесса и не влияет на родительскую оболочку. |
bash script.sh |
Нет | Новый процесс. Явно указан интерпретатор (bash), shebang игнорируется. | Аналогично ./script.sh — изоляция в новом процессе. |
source script.shили . script.sh |
Нет | Текущая оболочка. Команды скрипта выполняются так, как если бы вы ввели их вручную. | Переменные, alias, функции, изменение каталога — всё это сохраняется после завершения скрипта. Это мощно, но опасно для непроверенного кода. |
sh script.sh |
Нет | Новый процесс в оболочке sh (обычно dash, а не bash). |
Изоляция. Может привести к синтаксическим ошибкам, если скрипт использует специфичные для bash конструкции ([[ ]], {1..10}). |
Практический алгоритм диагностики
Когда сталкиваешься с отказом в запуске, системная диагностика экономит время. Вот проверенный порядок действий.
1. Проверка прав доступа
Выполни ls -l script.sh. В первой колонке ищи последовательность типа -rwxr-xr-x. Если для твоего пользователя (первые три символа после типа файла) отсутствует x, это причина ошибки.
Решение: chmod u+x script.sh — добавить право на исполнение только владельцу. Избегай chmod +x или, тем более, chmod 777, если скрипт используется в общей среде.
2. Проверка формата файла и shebang
Права есть, но скрипт не запускается? Проблема может быть в невидимых символах. Выполни head -1 script.sh | cat -A.
- Если в конце строки видишь
^M— это возврат каретки из Windows (CRLF). Используйdos2unix script.shилиsed -i 's/r$//' script.sh. - Убедись, что shebang корректен:
#!/bin/bashили#!/usr/bin/env bash. Второй вариант более переносим, так как ищет bash в переменной PATH.
3. Проверка доступности интерпретатора
Shebang указывает на /bin/bash, но в системе его нет по этому пути. Проверь: which bash или ls -l /bin/bash. Если пути различаются, исправь shebang или создай символьную ссылку.
4. Проверка синтаксиса
Интерпретатор может запуститься, но тут же завершиться с ошибкой из-за синтаксиса. Запусти проверку: bash -n script.sh. Ключ -n выполняет только синтаксический разбор без реального исполнения.
Опасные практики и безопасность
В корпоративной среде, особенно под требования 152-ФЗ и ФСТЭК, небрежность с правами доступа к скриптам создаёт уязвимости.
chmod 777 как антипаттерн
Команда chmod 777 script.sh даёт права на чтение, запись и исполнение всем пользователям системы, включая потенциально скомпрометированные учётные записи служб. Любой процесс с достаточными привилегиями может подменить содержимое скрипта на вредоносное. Минимально необходимый набор — 755 (владелец — всё, группа и другие — только чтение и исполнение). Для скриптов с чувствительными данными (пароли, ключи) лучше 700.
Запуск из непроверенных источников
Скачанный архив или вложение в письме — это зона риска. Перед запуском всегда проверяй содержимое: cat script.sh или less script.sh. Ищи подозрительные вызовы: curl | bash, rm -rf, chmod 777, запись в системные каталоги. Особенно опасно запускать такой код через source, так как он получит доступ ко всем переменным твоей сессии.
Скрипты от root и SUID-бит
Назначение бита SUID (chmod u+s script.sh) для скриптов в Linux обычно игнорируется из соображений безопасности. Если скрипт должен выполняться с привилегиями, используй sudo с явно прописанными правилами в /etc/sudoers, ограничивающими конкретные команды. Это обеспечивает подотчётность и принцип наименьших привилегий.
Когда использовать каждый метод запуска
Выбор метода — это выбор архитектуры выполнения.
./script.sh
Стандарт для готовых утилит и автоматизации. Предполагает, что скрипт самодостаточен, имеет корректный shebang и права. Все изменения изолированы. Используется в cron, systemd-сервисах, CI/CD-пайплайнах.
source script.sh или . script.sh
Применяется для загрузки настроек среды. Классические примеры: профиль оболочки (~/.bashrc), активация виртуального окружения Python, загрузка функций и alias. Помни, что exit в вызванном скрипте завершит всю текущую сессию терминала.
bash -x script.sh
Инструмент отладки. Ключ -x выводит каждую команду с её аргументами в стандартный поток ошибок (stderr) перед выполнением. Позволяет увидеть реальные значения переменных и логику ветвления. Для сложной отладки можно комбинировать с set -x внутри самого скрипта.
Запуск из другого скрипта или процесса
При вызове скрипта из другого скрипта или языка программирования (Python, Go) важно контролировать переменные среды и рабочий каталог. Используй абсолютные пути к скрипту. Передавай явные аргументы, а не полагайся на глобальное состояние. Учитывай коды возврата ($?) для обработки ошибок.
Понимание этих механизмов превращает ошибку «Permission denied» из раздражающего препятствия в осознанный сигнал о необходимости проверить контекст и безопасность планируемого действия.