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
"stderr" переходить до труби, але fd 1 закритий. Якщо cmd
спробує написати який-небудь вихід, він повернеться з EBADF
помилкою, якщо він відкриє файл, він отримає перший безкоштовний fd і відкритий файл йому буде призначений, stdout
якщо тільки команда не захищає цього! Не те, чого ми хочемо.
Якщо ми хочемо, щоб 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 залишається недоторканим. Однак ми просочуємо цей fd 3 to cmd
.
Хоча команди (за умовою) передбачають, що FDS 0 до 2 є відкритими і є стандартними введеннями, виводами та помилками, вони не припускають нічого іншого з інших FDS. Швидше за все, вони залишать цей fd 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 compound-command 3>&4-
або any-builtin 3>&4-
листя fd 4 закритий навіть після compound-command
або any-builtin
повернувся. Налагоджено виправлення для вирішення проблеми (2013-02-19).