Чтобы сохранить дескриптор файла, вы дублируете его на другом fd. Недостаточно сохранить путь к соответствующему файлу, вам нужно будет сохранить режим открытия, флаги открытия, текущую позицию в файле и так далее. И, конечно же, для анонимных каналов или сокетов это не сработает, поскольку у них нет пути.Вы хотите сохранить описание открытого файла , на которое ссылается fd, а дублирование fd фактически возвращает новый fd в то же описание открытого файла .
Чтобы скопировать дескриптор файла на другой в оболочке, подобной Bourne, синтаксис следующий:
exec 3>&1
Выше fd 1 дублируется на fd 3.
Любой fd 3, который уже был открыт ранее, будет закрыт, но обратите внимание, что fds от 3 до 9 (обычно больше, до 99 с yash
) зарезервированы для этой цели (и не имеют специального значения в отличие от 0, 1 или 2), оболочка знает, что их не использовать для собственного внутреннего бизнеса. Единственная причина, по которой fd 3 был бы открыт заранее, состоит в том, что вы сделали это в скрипте 1 , или он был пропущен вызывающей стороной.
Затем вы можете изменить stdout на что-нибудь другое:
exec > /dev/null
И позже, чтобы восстановить stdout:
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
Здесь оболочка заботится о сохранении stdout и его последующем восстановлении (и делает это внутренне дублируя его на fd (выше 9, выше 99 для yash
) с установленным флагом close-on-exec ).
Теперь управление этими fds с 3 по 9 может быть обременительным и проблематичным, если вы используете их широко или в функциях, особенно если ваш скрипт использует сторонний код, который, в свою очередь, может использовать эти fds.
Некоторые оболочки ( zsh
, bash
, ksh93
, все добавили функцию (, предложенную Оливером Киддлом из 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}>&-
}