Ошибка write: bad file descriptor 9 появляется в терминале Linux или Unix-подобной системы в тот момент, когда процесс пытается записать данные в файловый дескриптор, который уже закрыт, никогда не открывался или был открыт только для чтения. Чаще всего её можно увидеть в выводе bash-скриптов, при работе с перенаправлением ввода-вывода, в логах Docker-контейнеров и при вызовах системных утилит вроде curl, ssh или git.
Цифра 9 в сообщении — это номер конкретного дескриптора, с которым произошёл сбой. В Unix-системах каждому открытому файлу, сокету или каналу ядро присваивает целочисленный идентификатор: 0 — стандартный ввод, 1 — стандартный вывод, 2 — поток ошибок, а номера от 3 и выше выделяются файлам и соединениям, которые открывает сама программа. Дескриптор 9 нередко используется в shell-скриптах как «свободный» номер для собственных перенаправлений, поэтому именно он часто фигурирует в тексте ошибки.
Что такое файловый дескриптор и почему он становится «плохим»
Файловый дескриптор — это абстракция ядра операционной системы, через которую процесс обращается к файлам, сокетам, каналам (pipes) и устройствам. Когда программа вызывает системную функцию open() или socket(), ядро возвращает номер дескриптора, и дальнейшие операции чтения и записи выполняются уже через этот номер.
Ошибка EBADF (bad file descriptor) возникает в трёх типовых ситуациях. Первая — запись в дескриптор, который уже был закрыт вызовом close() или конструкцией exec 9>&-. Вторая — попытка писать в дескриптор, открытый в режиме «только чтение». Третья — обращение к номеру, который вообще не был открыт в текущем процессе, например из-за опечатки в скрипте.
Стоит понимать: номера дескрипторов локальны для каждого процесса. Дескриптор 9 в родительской оболочке и дескриптор 9 в дочернем процессе — это разные сущности, если дочерний процесс не унаследовал его явно. Именно потеря наследования при запуске через sudo, nohup или планировщик — частая скрытая причина сбоя.
Типичные сценарии появления ошибки
На практике проблема чаще всего проявляется в нескольких узнаваемых контекстах. Ниже — ситуации, которые стоит проверить в первую очередь.
- 🔧 В bash-скрипте используется конструкция echo "text" >&9, но дескриптор 9 не был предварительно открыт командой exec 9>файл.
- 📜 Логирование в скрипте настроено через перенаправление, а блок exec с открытием дескриптора находится в условной ветке, которая не выполнилась.
- 🐳 Приложение в Docker-контейнере пытается писать в сокет или файл, который был закрыт другим потоком — типично для многопоточных программ при гонке состояний.
- 🔌 Скрипт запускается через sudo или su, и новая сессия не наследует дескрипторы, открытые в исходной оболочке.
- 🧹 Внешняя утилита или таймаут-обёртка закрывает лишние дескрипторы при запуске дочернего процесса.
Отдельный случай — использование bash-фичи /dev/tcp или /dev/udp для сетевых соединений прямо из скрипта. Здесь дескриптор 9 часто назначают сокету вручную, и если соединение оборвалось по таймауту или удалённая сторона закрыла его, следующая попытка записи даст именно эту ошибку.
📊 Где вы столкнулись с ошибкой bad file descriptor 9?