Как перенаправить дескриптор выходного файла подоболочки на дескриптор входного файла в родительской оболочке?

(В BASH) я хочу, чтобы подоболочка использовала дескриптор файла, отличный от STDOUT, отличный от STDERR, для передачи некоторых данных обратно в родительскую оболочку. Как я могу это сделать? В конце концов я хотел бы сохранить данные в какую-то переменную родительской оболочки.

(
  # The following two lines show the behavior of the subshell.
  # We cannot change them.
  echo "This should go to STDOUT"
  echo "This is the data I want to pass to the parent shell" >&3
)
#...
data_from_subshell=... # Somehow assign the value of &3 of the
                       # subshell to this variable

РЕДАКТИРОВАТЬ: подоболочка запускает программу черного ящика, которая записывает в STDOUT и &3.


person user716468    schedule 01.03.2013    source источник


Ответы (2)


ОСТОРОЖНО, ВПЕРЕДИ БАШИЗМ (существуют оболочки posix, которые значительно быстрее, чем bash, например, ash или dash, в которых нет подстановки процессов).

Вы можете сделать танец с ручкой, чтобы переместить исходный стандартный вывод в новый дескриптор, чтобы сделать стандартный вывод доступным для конвейера (из головы):

exec 3>&1 # creates 3 as alias for 1
run_in_subshell() { # just shortcut for the two cases below
    echo "This goes to STDOUT" >&3
    echo "And this goes to THE OTHER FUNCTION"
}

Теперь вы должны быть в состоянии написать:

while read line; do
    process $line
done < <(run_in_subshell)

но конструкция <() — это башизм. Вы можете заменить его конвейером

run_in_subshell | while read line; do
    process $line
done

за исключением того, что вторая команда также выполняется в подоболочке, потому что все команды в конвейере работают.

person Jan Hudec    schedule 01.03.2013
comment
Ваше решение очень вдохновляет! Я думаю, что вполне может это сделать. Я только что отправил редактирование этого решения. Поменяв местами &1 и &3 вне подоболочки, мы можем избежать изменения исходной подоболочки. Однако я не уверен, что ваше второе решение (с | ) может работать, потому что цикл while после канала также является подоболочкой в ​​BASH. - person user716468; 01.03.2013
comment
@ user716468: Вам придется поменять местами &3 и &1 внутри подоболочки, потому что подстановка канала и процесса может принимать только &1. Да, в более позднем цикл также выполняется в подоболочке. К сожалению, это все, что вы можете получить от не-bash. Это немного мучительно, но поскольку родительский и дочерний элементы в основном эквивалентны, часто вы можете просто переместить остальную часть скрипта в принимающую команду в конвейере. - person Jan Hudec; 01.03.2013
comment
Конечно, решение bash работает и в других оболочках, таких как zsh. Но он не работает в более простых оболочках, которые часто используются как /bin/sh из соображений производительности. - person Jan Hudec; 01.03.2013
comment
Отказ от ответственности за башизм очень полезен! - person Roy Truelove; 23.02.2016
comment
танцуй с ручкой - я чувствую, что именно так я открываю дверь своей машины :D обожаю это - person Peter M. Elias; 23.08.2019

Самый простой способ, конечно, - захватить вывод непосредственно в родительском

data_from_subshell=$(echo "This is the data I want to pass to the parent shell")

Вы можете использовать именованный канал как альтернативный способ чтения данных от дочернего элемента.

mkfifo /tmp/fifo

теперь вы можете перенаправить ребенка на /tmp/fifo

(
    echo "This should go to STDOUT"
    echo "This is the data I want to pass to the parent shell" >/tmp/fifo
) &

и родитель может читать оттуда

read data_from_subshell </tmp/fifo

Другой способ — использовать coproc для запуска дочернего процесса. Это создает дочерний элемент с двунаправленным каналом и перенаправляет дочерний стандартный ввод и стандартный вывод на дескрипторы канала. Чтобы использовать и канал, и стандартный вывод в дочернем элементе, вы должны сначала продублировать стандартный вывод в родительском.

exec 4>&1 # duplicate stdout for usage in client

coproc SUBSHELL (
    exec 3>&1 1>&4- # redirect fd 3 to pipe, redirect fd 1 to stdout
    (
    echo "This should go to STDOUT"
    echo "This is the data I want to pass to the parent shell" >&3
    )
)

exec 4>&- # close fd 4 in parent
read data <&${SUBSHELL[0]}
echo "Parent: $data"

Сопроцессы были представлены в Bash 4.0.

person Olaf Dietsche    schedule 01.03.2013
comment
Переменная, которая фиксирует данные в подоболочке, не видна родительской оболочке. - person user716468; 01.03.2013
comment
При использовании именованных каналов подоболочка должна запускаться одновременно с родительской оболочкой (используя &), иначе она будет заблокирована в ›/tmp/fifo. В этом случае требуется дополнительная синхронизация. - person user716468; 01.03.2013
comment
@user716468 user716468 Конечно, я никогда не собирался собирать данные в подоболочке. Я уточнил свой ответ. Если вы уже знаете ответ на свой вопрос, зачем вообще спрашиваете? - person Olaf Dietsche; 01.03.2013
comment
Спасибо за ваши ответы и разъяснения. Нет, я еще не нашел ответа. Переменный способ может не работать, так как я хочу, чтобы подоболочка использовала STDOUT для печати чего-то еще, в то же время передавая некоторые данные родителю. - person user716468; 01.03.2013
comment
Разве здесь не должно быть достаточно подстановки процессов? - person Jan Hudec; 01.03.2013
comment
@JanHudec Фактически то же самое, просто другой синтаксис. Дочерний процесс пишет как в стандартный вывод, так и в канал. - person Olaf Dietsche; 01.03.2013
comment
@user716468 user716468 Требуется немного повозиться с файловыми дескрипторами, но вы можете все уладить так, чтобы они подходили. Пожалуйста, смотрите измененный ответ. - person Olaf Dietsche; 01.03.2013
comment
@user4838443 user4838443 Спасибо, я добавил ваш намек на версию Bash в свой ответ. - person Olaf Dietsche; 05.05.2015