Дескриптор файла не закрыт, но перемещен.

Вопрос или проблема

Согласно 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. Однако, когда эта команда выполняется в теле блока кода (в фигурных скобках), происходит несколько интересных вещей:

  1. Текущий контекст: При выполнении команды в теле блока использует свой собственный контекст, и изменения, сделанные с дескрипторами файлов, могут не отражаться на родительском процессе так, как вы ожидаете.
  2. Проблема с доступом: Когда вы пытаетесь записать в дескриптор 6 после его закрытия, возникает ошибка "Bad file descriptor", что подтверждает, что дескриптор 6 действительно закрыт в этом контексте.

Почему вывод fck2 происходит, несмотря на закрытие дескриптора?

Когда вы вызываете 6>&- внутри блока, это приводит к закрытию дескриптора 6 для текущего блока, но не в глобальном контексте функции func. Поэтому, когда вы вызываете echo fck2 >&10, происходит следующее:

  • Во время выполнения блока 6>&-, дескриптор 6 закрывается. Однако, если к этому моменту вы уже переместили некоторые потоки вывода (например, вывод с помощью >&10), то часть данных может быть направлена в другой дескриптор, как это и происходит в вашем примере.
  • Высветив вывод fck2, вы обнаруживаете, что он был записан на дескриптор, который был ранее определен (в вашем случае 10), но не на закрытый 6.

Использование подсобных оболочек

Как вы также заметили, использование подсобной оболочки ( ... ) позволяет корректно закрыть дескриптор 6. Причина в том, что подсобные оболочки создают новый уровень контекста, и изменения, сделанные в этой оболочке, не влияют на родительский процесс. То есть:

( … ) 6>&-

Создает изолированный контекст, и закрытие дескриптора происходит только в этой оболочке.

Заключение

Таким образом, вывод fck2 происходит из-за ожидаемого поведения перемещения дескриптора и изолированных контекстов исполнения в Bash. Важно понимать, как контексты и дескрипторы работают в Bash, чтобы избегать подобного поведения в будущем. Если вам нужно полностью закрыть дескриптор во всей функции, стоит использовать подсобные оболочки или пересмотреть подход к управлению дескрипторами.

Надеюсь, это объяснение окажется полезным!

Оцените материал
Добавить комментарий

Капча загружается...