перенаправление дескриптора выходного файла

#ksh #file-descriptor

#ksh #файловый дескриптор

Вопрос:

Мне нужно иметь возможность перенаправлять вывод в файл, а также проверять код возврата скрипта в KSH (не могу использовать pipestatus или pipefail), я нашел решение, но я не уверен, в чем значение file discriptor 4, может кто-нибудь объяснить, пожалуйста?

 {
rc=$(
{
  {
    . somescript.sh 2>amp;1
    echo "$?" >amp;3
  } | tee -a somefile.txt
} 3>amp;1 >amp;4 4>amp;-
)
} 4>amp;1

echo "${rc}"
  

Ответ №1:

rc=$(...) означает, что код возврата будет тем, что напечатано в файловом дескрипторе (fd) 1 кодом внутри (...) . Итак, каким-то образом, какие somescript.sh выходные данные должны быть удалены из fd 1 , а затем возвращены позже. echo Строка выводит somescript.sh код возврата в fd 3 . Затем 3>amp;1 отправляет сохраненный код возврата в fd 1 , где $(...) его ожидает. Однако это означает, что старому fd 1 (from {somescript 2>amp;1 } | tee ) некуда идти. Таким образом, старый fd 1 перенаправляется на fd 4 с >amp;4 помощью (а сторона ввода закрывается с 4>amp;- помощью, поскольку она не будет использоваться). Затем, после $(...) завершения, 4>amp;1 в конце выводится somescript|tee обратно в fd 1 , где его ожидают другие программы.

Фух!

Без >amp;4 этого выходные somescript.sh данные и выходные echo "$?" данные были бы смешаны в fd 1 из-за 3>amp;1 . Таким образом, fd 4 — это удерживающее перо для фактического вывода somescript.sh в течение времени, в течение которого fd 1 используется для переноса кода возврата.

Комментарии:

1. Я всегда думал, что $(…) считывается из стандартного вывода, на что обычно указывает и fd 1 , но если он считывается из fd 1, тогда это имело бы большой смысл. Спасибо.

2. fd 1 не «указывает» на стандартный вывод; это стандартный вывод.

3. @chepner ах, хорошо, спасибо за разъяснение, и обычно стандартный вывод указывает на терминал.

Ответ №2:

Если вы хотите использовать именованный канал, вы можете отказаться от всех споров с файловыми дескрипторами:

 mkfifo p
tee -a somefile.txt < p amp;
. somescript.sh > p
rc=$?
  

Здесь мы выполняем tee в фоновом режиме, позволяя ему считывать входные данные из именованного канала p . Как только это задание запускается, мы отправляем скрипт и перенаправляем его вывод в именованный канал. После завершения работы скрипта вы можете сохранить его статус завершения с rc помощью обычного оператора присваивания. Это также закроет конец канала оболочки, что также приведет к закрытию другого конца и позволит tee выйти.

Комментарии:

1. Если бы вы запустили это с помощью fd 1 , изначально указывающего на терминал, это tee ... amp; привело бы tee к остановке с помощью a SIGTTOU при somescript.sh запуске вывода на p ?

2. Теоретически, да, но ksh должно быть переопределено действие по умолчанию SIGTTOU , чтобы разрешить tee прямую запись в терминал. (Честно говоря, мне пришлось посмотреть, что SIGTTOU было, потому что я не заметил никаких проблем, когда тестировал это.)

3.Кнут «Остерегайтесь ошибок в приведенном выше коде; я только доказал его правильность, а не пробовал». 🙂 Спасибо!