В bash, как зафиксировать некоторый вывод в переменной, позволяя остальным перейти к стандартному выводу

Я знаю о возможности вывода захвата bash в соответствии со строками (два отдельных файла):

sub.sh:     echo hello
main.sh:    greeting="$(./sub.sh)"

Это установит для переменной greeting значение hello.

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

sub.sh:     xyzzy hello ; plugh goodbye
main.sh:    greeting="$(./sub.sh)"

Я бы хотел, чтобы hello было помещено в переменную greeting, а goodbye было отправлено на стандартный вывод main.sh.

Что нужно заменить волшебными командами xyzzy и plugh выше (или что я могу сделать в main.sh), чтобы добиться такого поведения? Я подозреваю, что это можно сделать с помощью какой-то хитрой возни с перенаправлениями на основе дескрипторов, но я не уверен. Если это невозможно, мне придется вернуться к записи одного из элементов во временный файл, который будет загружен позже, но я бы предпочел этого не делать.


Чтобы было понятнее, вот тестовый пример, который я использую (в настоящее время использую метод нерабочего дескриптора файла 3). Первый sub.sh:

echo xx_greeting >&3 # This should be captured to variable.
echo xx_stdout       # This should show up on stdout.
echo xx_stderr >&2   # This should show up on stderr.

Затем main.sh:

greeting="$(./sub.sh)" 3>&1
echo "Greeting was ${greeting}"

И я запускаю это так:

./main.sh >/tmp/out 2>/tmp.err

ожидаем увидеть следующие файлы:

/tmp/out:
    xx_stdout
    Greeting was xx_greeting 
/tmp/err:
    xx_stderr

person paxdiablo    schedule 26.10.2020    source источник
comment
Каждый файловый дескриптор указывает только на одно место, когда программа выполняется, даже если это место является стандартным вводом отдельной программы, которая выполняет причудливую логику (например, чтение первой строки, а затем пересылка остальных в другое место).   -  person Charles Duffy    schedule 26.10.2020
comment
Используйте дополнительный fd: sub.sh: echo hello; echo nasty >&3, main.sh: exec 3>&1; var=$(./sub.sh).   -  person user414777    schedule 26.10.2020


Ответы (1)


Это можно сделать, введя дополнительный файловый дескриптор следующим образом. Сначала скрипт sub.sh для тестирования, который просто записывает разные вещи в три разных дескриптора (неявный >&1 для первого):

echo for-var
echo for-out >&3
echo for-err >&2

Во-вторых, main.sh, который вызывает это:

exec 3>&1
greeting="$(./sub.sh)"
echo "Variable is ${greeting}"

Затем вы просто запускаете его, гарантируя, что знаете, какой вывод идет в разные места:

pax> ./main.sh > xxout 2> xxerr

pax> cat xxout
for-out
Variable is for-var

pax> cat xxerr
for-err

Следовательно, вы можете видеть, что при вызове sub.sh из main.sh данные, записанные в дескриптор файла 1, передаются в переменную захвата, данные, записываемые в дескриптор файла 2, отправляются в стандартную ошибку, а данные, записываемые в дескриптор файла 3, отправляются в стандартный вывод.

person paxdiablo    schedule 26.10.2020