3>&4-
это расширение ksh93, также поддерживаемое bash, и оно сокращенно 3>&4 4>&-
, то есть 3 теперь указывает на то место, где раньше использовалось 4, а 4 теперь закрыто, поэтому то, на что указывал 4, теперь переместилось на 3.
Типичное использование будет в тех случаях, когда вы дублируете stdin
или stdout
сохраняете копию и хотите восстановить ее, как в:
Предположим, вы хотите перехватить stderr команды (и только stderr), оставив stdout в одной переменной.
Подстановка команды var=$(cmd)
, создает канал. Конец записи канала становится cmd
stdout (дескриптор файла 1), а другой конец читается оболочкой для заполнения переменной.
Теперь, если вы хотите , stderr
чтобы перейти к переменной, вы можете сделать: var=$(cmd 2>&1)
. Теперь и fd 1 (stdout), и 2 (stderr) переходят в канал (и, в конечном итоге, к переменной), что составляет лишь половину того, что мы хотим.
Если мы делаем var=$(cmd 2>&1-)
(сокращение от var=$(cmd 2>&1 >&-
), теперь только cmd
канал 's stderr' идет в канал, но fd 1 закрыт. Если cmd
попытается записать какой-либо вывод, который вернется с EBADF
ошибкой, если он откроет файл, он получит первый свободный fd, и открытый файл будет назначен ему, stdout
если только команда не защитит от этого! Не то, что мы хотим.
Если мы хотим, чтобы стандартный вывод cmd
был оставлен в покое, то есть указывал на тот же ресурс, на который он указывал вне подстановки команд, то нам нужно каким-то образом перенести этот ресурс в подстановку команд. Для этого мы можем сделать копию stdout
внешней подстановки команды, чтобы взять ее внутрь.
{
var=$(cmd)
} 3>&1
Какой способ написания чище:
exec 3>&1
var=$(cmd)
exec 3>&-
(который также имеет преимущество восстановления fd 3 вместо закрытия в конце).
Затем на {
(или exec 3>&1
) и до }
, оба fd 1 и 3 указывают на один и тот же ресурс, на который fd 1 указал изначально. fd 3 также будет указывать на этот ресурс внутри подстановки команд (подстановка команд перенаправляет только fd 1, stdout). Итак, выше, cmd
у нас есть для fds 1, 2, 3:
- труба к вар
- нетронутый
- так же, как то, что 1 указывает на замещение команды
Если мы изменим это на:
{
var=$(cmd 2>&1 >&3)
} 3>&1-
Тогда это становится:
- так же, как то, что 1 указывает на замещение команды
- труба к вар
- так же, как то, что 1 указывает на замещение команды
Теперь у нас есть то, что мы хотели: stderr идет в трубу, а stdout остается нетронутым. Тем не менее, мы просочились в фд 3 к cmd
.
Хотя команды (по соглашению) предполагают, что fds 0 - 2 открыты и являются стандартным вводом, выводом и ошибкой, они не предполагают ничего из других fds. Скорее всего, они оставят этот 3 нетронутым. Если им нужен другой файловый дескриптор, они просто сделают, open()/dup()/socket()...
который вернет первый доступный файловый дескриптор. Если (как и скрипт оболочки, который делает это exec 3>&1
) им нужно использовать это fd
специально, они сначала назначат его чему-то (и в этом процессе ресурс, удерживаемый нашим fd 3, будет освобожден этим процессом).
Рекомендуется закрывать этот fd 3, поскольку cmd
он не используется, но нет ничего страшного, если мы оставим его назначенным до вызова cmd
. Проблемы могут заключаться в том, что cmd
(и, возможно, другие процессы, которые он порождает) имеет меньше доступного fd. Потенциально более серьезная проблема заключается в том, что ресурс, на который указывает этот fd, может в конечном итоге удерживаться процессом, порожденным этим cmd
в фоновом режиме. Это может быть проблемой, если этот ресурс является каналом или другим каналом межпроцессного взаимодействия (например, когда ваш скрипт выполняется как script_output=$(your-script)
), так как это будет означать, что чтение процесса с другого конца никогда не будет видеть конец файла до тех пор, пока фоновый процесс завершается.
Итак, здесь лучше написать:
{
var=$(cmd 2>&1 >&3 3>&-)
} 3>&1
Который bash
может быть сокращен до:
{
var=$(cmd 2>&1 >&3-)
} 3>&1
Подводя итог, почему это редко используется:
- это нестандартный и просто синтаксический сахар. Вы должны сбалансировать сохранение нескольких нажатий клавиш, чтобы сделать ваш сценарий менее переносимым и менее очевидным для людей, не привыкших к этой необычной функции.
- Необходимость закрытия исходного fd после дублирования часто упускается из виду, потому что большую часть времени мы не страдаем от последствий, поэтому мы просто делаем
>&3
вместо >&3-
или >&3 3>&-
.
Доказательством того, что он редко используется, как вы узнали, является то, что это фальшивка в bash . В bash compound-command 3>&4-
или any-builtin 3>&4-
листья fd 4 закрыты даже после compound-command
или any-builtin
вернулся. Патч , чтобы исправить эту проблему сейчас (2013-02-19) доступен.
{ var=$(cmd 2>&1 >&3) ; } 3>&1-
Разве это не опечатка в закрытии 1?$(...)
).{...}
fd 3 указывает на то, что fd 1 использовалось для указания, а fd 1 закрывается, затем при входе$(...)
fd 1 устанавливается на трубу, которая подает$var
, затем также наcmd
2 на эту, а затем 1 на 3 точки к, это внешний 1. Тот факт, что 1 остается закрытым, является ошибкой в bash, я сообщу об этом. ksh93, откуда взялась эта особенность, не имеет этой ошибки.Это означает, что он указывает на то же место, что и другой дескриптор файла. Вы должны сделать это очень редко, кроме очевидной отдельной обработки стандартного дескриптора ошибок (
stderr
,fd 2
,/dev/stderr -> /proc/self/fd/2
). Это может пригодиться в некоторых сложных случаях.Руководство по расширенному написанию сценариев Bash содержит более длинный пример уровня журнала и этот фрагмент:
В «Волшебстве исходного мага» мы, например, используем его, чтобы различить разные выводы из одного и того же блока кода:
Он имеет дополнительную подстановку процесса, добавленную по причинам регистрации (VOYEUR решает, должны ли данные отображаться на экране или просто в журнале), но некоторые сообщения должны всегда отображаться. Для этого мы печатаем их в файловый дескриптор 3, а затем обрабатываем их специально.
источник
В Unix файлы обрабатываются дескрипторами файлов (маленькие целые числа, например стандартный ввод равен 0, стандартный вывод равен 1, стандартная ошибка равна 2; при открытии других файлов им обычно назначается наименьший неиспользуемый дескриптор). Поэтому, если вы знаете внутреннюю часть программы и хотите отправить выходные данные, которые идут в файловый дескриптор 5, в стандартный вывод, вы должны переместить дескриптор 5 на 1. Вот откуда это
2> errors
происходит, и конструкции любят2>&1
дублировать ошибки в выходной поток.Так что вряд ли когда-либо использовался (я смутно помню, как использовал его один или два раза в гневе за мои 25 с лишним лет почти исключительного использования Unix), но когда это необходимо, абсолютно необходимо.
источник
5>&1
отправляет 5 туда, куда идет 1, то что именно делает1>&5-
, кроме закрытия 5?