Вопрос или проблема
Согласно man bash
, перенаправление 6>&-
должно закрывать дескриптор файла 6
, но кажется, что он только перемещается (в данном случае на 10
). Почему это так?
#!/usr/bin/env bash
set -u
func() {
echo fck0 >&10
{
echo fck1 >&6
echo fck2 >&10
} 6>&-
echo yes >&6
}
func 6>&1
Вывод:
/tmp/t2: line 6: 10: Неверный дескриптор файла
/tmp/t2: line 8: 6: Неверный дескриптор файла
fck2
yes
Очевидно, что вывод fck2
неожидаем.
Такое же поведение можно наблюдать, если используется синтаксис {varname}
.
Однако это работает, если используется подсhell, т.е. ( … ) 6>&-
.
Ответ или решение
Существующая проблема с дескрипторами файлов в Bash, приведенная в вашем вопросе, действительно может вызвать некоторые недоразумения. Давайте подробно рассмотрим все аспекты данной ситуации, чтобы понять, почему происходит "перемещение" дескриптора, а не его закрытие.
Понимание deskriptorов файлов в Bash
В Unix-подобных системах каждый процесс имеет набор дескрипторов файлов, которые позволяют ему взаимодействовать с различными объектами, такими как файлы, каналы и устройства. Дескрипторы 0, 1 и 2 обычно используются для стандартного ввода (stdin), стандартного вывода (stdout) и стандартного вывода ошибок (stderr) соответственно. Остальные дескрипторы могут быть назначены по необходимости.
Закрытие и перемещение дескрипторов
Ваша команда 6>&-
должна theoretically закрыть дескриптор 6. Однако, когда эта команда выполняется в теле блока кода (в фигурных скобках), происходит несколько интересных вещей:
- Текущий контекст: При выполнении команды в теле блока использует свой собственный контекст, и изменения, сделанные с дескрипторами файлов, могут не отражаться на родительском процессе так, как вы ожидаете.
- Проблема с доступом: Когда вы пытаетесь записать в дескриптор 6 после его закрытия, возникает ошибка "Bad file descriptor", что подтверждает, что дескриптор 6 действительно закрыт в этом контексте.
Почему вывод fck2
происходит, несмотря на закрытие дескриптора?
Когда вы вызываете 6>&-
внутри блока, это приводит к закрытию дескриптора 6 для текущего блока, но не в глобальном контексте функции func
. Поэтому, когда вы вызываете echo fck2 >&10
, происходит следующее:
- Во время выполнения блока
6>&-
, дескриптор 6 закрывается. Однако, если к этому моменту вы уже переместили некоторые потоки вывода (например, вывод с помощью>&10
), то часть данных может быть направлена в другой дескриптор, как это и происходит в вашем примере. - Высветив вывод
fck2
, вы обнаруживаете, что он был записан на дескриптор, который был ранее определен (в вашем случае 10), но не на закрытый 6.
Использование подсобных оболочек
Как вы также заметили, использование подсобной оболочки ( ... )
позволяет корректно закрыть дескриптор 6. Причина в том, что подсобные оболочки создают новый уровень контекста, и изменения, сделанные в этой оболочке, не влияют на родительский процесс. То есть:
( … ) 6>&-
Создает изолированный контекст, и закрытие дескриптора происходит только в этой оболочке.
Заключение
Таким образом, вывод fck2
происходит из-за ожидаемого поведения перемещения дескриптора и изолированных контекстов исполнения в Bash. Важно понимать, как контексты и дескрипторы работают в Bash, чтобы избегать подобного поведения в будущем. Если вам нужно полностью закрыть дескриптор во всей функции, стоит использовать подсобные оболочки или пересмотреть подход к управлению дескрипторами.
Надеюсь, это объяснение окажется полезным!