Тож я хотів надати відповідь на кшталт лесмана, але я думаю, що моя, можливо, трохи простіше і трохи вигідніше рішення з чистою оболонкою Борна:
# You want to pipe command1 through command2:
exec 4>&1
exitstatus=`{ { command1; printf $? 1>&3; } | command2 1>&4; } 3>&1`
# $exitstatus now has command1's exit status.
Я думаю, що це найкраще пояснюється зсередини - command1 виконає та надрукує свій звичайний вихід на stdout (файловий дескриптор 1), після чого, виконавши printf, він виконає та надрукує код виходу icommand1 на свій stdout, але цей stdout буде переспрямований на дескриптор файлу 3.
Поки команда1 працює, її stdout передається команді2 (вихід printf ніколи не робить його командою2, тому що ми надсилаємо його дескриптору файлів 3 замість 1, який читає труба). Тоді ми перенаправляємо висновок command2 на дескриптор файлу 4, так що він також залишається поза дескриптором 1 файлу - тому що ми хочемо дескриптор файлу 1 безкоштовно трохи пізніше, тому що ми повернемо результат printf на дескриптор файлу 3 назад вниз в дескриптор файлу 1 - тому що це те, що захоплює підміна команд (backticks), і саме це буде розміщено у змінній.
Останній шматочок магії - це перший exec 4>&1
ми зробили як окрему команду - він відкриває дескриптор файлу 4 як копію викладу зовнішньої оболонки. Заміна команд охоплює все, що написано стандартно, з точки зору команд, що знаходяться всередині неї, але оскільки вихід команд2 збирається в дескриптор 4 файлів, що стосується підстановки команд, підміна команди не захоплює його - однак колись це "виходить" з підстановки команди, вона фактично все ще переходить до загального дескриптора файлу сценарію 1.
(Це exec 4>&1
повинно бути окремою командою, оскільки багатьом звичайним оболонкам це не подобається, коли ви намагаєтесь записати в дескриптор файлу всередині підстановки команди, що відкривається у "зовнішній" команді, яка використовує підстановку. Отже, це найпростіший портативний спосіб це зробити.)
Ви можете дивитись на це менш технічним та грайливим способом, як якщо б виходи команд перескакували один одного: command1 передає команду2, тоді вихід printf перескакує команду 2, щоб команда2 не впіймала її, а потім Вихід команди 2 перескакує та виходить із підстановки команд так само, як printf висаджується саме вчасно, щоб потрапити в полон підстановкою, щоб вона опинилася в змінній, а результат команд2 продовжує веселий спосіб записуватися до стандартного виводу так само, як у звичайній трубі.
Крім того, як я розумію, він $?
все ще буде містити код повернення другої команди в трубі, оскільки змінні призначення, підстановки команд і складені команди всі ефективно прозорі до повернення коду команди всередині них, тому стан повернення command2 повинен бути розповсюджений - це, і не потрібно визначати додаткову функцію, саме тому я думаю, що це може бути дещо кращим рішенням, ніж те, запропоноване лесмана.
Згідно з згадуваною леманою застереження, можливо, що команда1 в якийсь момент опиниться за допомогою дескрипторів файлів 3 або 4, щоб бути більш надійними, ви зробите:
exec 4>&1
exitstatus=`{ { command1 3>&-; printf $? 1>&3; } 4>&- | command2 1>&4; } 3>&1`
exec 4>&-
Зауважте, що я використовую складні команди у своєму прикладі, але підзаголовки (використання ( )
замість { }
також буде працювати, хоча може бути менш ефективним.)
Команди успадковують дескриптори файлів із процесу, який запускає їх, тому весь другий рядок успадковуватиме дескриптор файлу чотири, а складна команда, за якою слідує, 3>&1
успадкує дескриптор файлу три. Таким чином, 4>&-
переконайтеся, що внутрішня складова команда не успадкує дескриптор файлу чотири, а 3>&-
також не буде успадковувати дескриптор файлу три, тому command1 отримує "більш чисте", більш стандартне середовище. Ви також можете перемістити внутрішній 4>&-
поруч із 3>&-
, але я вважаю, чому б не просто максимально обмежити його обсяг.
Я не впевнений, як часто речі використовують дескриптор файлів три та чотири безпосередньо - я думаю, що більшість часу програми використовують систематичні дзвінки, які повертають не використовувані в даний момент дескриптори файлів, але іноді код записується в дескриптор файлу 3 безпосередньо, я здогадуюсь (я міг би уявити програму, яка перевіряє дескриптор файлу, щоб побачити, чи він відкритий, і використовувати його, якщо він є, або поводитись відповідно інакше, якщо його немає). Тому останнє, мабуть, найкраще мати на увазі та використовувати для загальних справ.