Чтобы сохранить дескриптор файла, вы дублируете его на другом диске. Сохранение пути к соответствующему файлу недостаточно, вам нужно сохранить режим открытия, флаги открытия, текущую позицию в файле и так далее. И, конечно же, для анонимных каналов или сокетов это не сработает, поскольку у них нет пути. Вы хотите сохранить описание открытого файла , на которое ссылается fd, а дублирование fd фактически возвращает новый fd к тому же описанию открытого файла .
Чтобы дублировать файловый дескриптор на другой с помощью Bourne-подобной оболочки, синтаксис:
exec 3>&1
Выше fd 1 дублируется на fd 3.
То, что fd 3 уже было открыто до того, будет закрыто, но учтите, что fds 3–9 (обычно больше, до 99 с yash
) зарезервированы для этой цели (и не имеют специального значения, противоположного 0, 1 или 2), Оболочка знает, чтобы не использовать их для собственного внутреннего бизнеса. Единственная причина, по которой fd 3 был бы открыт заранее - это то, что вы сделали это в сценарии 1 или он был пропущен вызывающей стороной.
Затем вы можете изменить стандартный вывод на другое:
exec > /dev/null
И позже, чтобы восстановить стандартный вывод:
exec >&3 3>&-
( 3>&-
чтобы закрыть дескриптор файла, который нам больше не нужен).
Теперь проблема в том, что кроме ksh, каждая команда, которую вы запускаете после этого exec 3>&1
, наследует этот fd 3. Это утечка fd. Как правило, не имеет большого значения, но это может вызвать проблемы.
ksh
устанавливает флаг close-on-exec для этих fds (для fds более 2), но никакие другие оболочки и другие оболочки не имеют никакого способа установить этот флаг вручную.
Обходной путь для другой оболочки - закрыть fd 3 для каждой команды, например:
exec 3>&-
exec > file.log
ls 3>&-
uname 3>&-
exec >&3 3>&-
Громоздкие. Здесь лучшим способом было бы вообще не использовать exec
, а перенаправлять группы команд:
{
ls
uname
} > file.log
Там это оболочка, которая заботится о сохранении стандартного вывода и его последующем восстановлении (и делает это внутренне, дублируя его на fd (выше 9, выше 99 для yash
) с установленным флагом close-on-exec ).
Примечание 1
Теперь управление этими fds с 3 по 9 может быть громоздким и проблематичным, если вы используете их широко или в функциях, особенно если ваш скрипт использует какой-то сторонний код, который, в свою очередь, может использовать эти fds.
Некоторые оболочки ( zsh
, bash
, ksh93
, все добавили функцию ( предложенный Oliver Киддл изzsh
) примерно в то же время в 2005 году после того, как он был обсужден среди их разработчиков) имеют альтернативный синтаксис , чтобы назначить первый свободный FD выше 10 вместо , который помогает в этом случае:
myfunction() {
local fd
exec {fd}>&1
# stdout was duplicated onto a new fd above 10, whose actual value
# is stored in the fd variable
...
# it should even be safe to re-enter the function here
...
exec >&"$fd" {fd}>&-
}