#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
к остановке с помощью aSIGTTOU
приsomescript.sh
запуске вывода наp
?2. Теоретически, да, но
ksh
должно быть переопределено действие по умолчаниюSIGTTOU
, чтобы разрешитьtee
прямую запись в терминал. (Честно говоря, мне пришлось посмотреть, чтоSIGTTOU
было, потому что я не заметил никаких проблем, когда тестировал это.)3.Кнут «Остерегайтесь ошибок в приведенном выше коде; я только доказал его правильность, а не пробовал». 🙂 Спасибо!