Использование собственных инструментов диалога: --output-fd flag
Если вы читаете man-страницу для диалога, есть опция --output-fd
, которая позволяет вам явно указать, куда выводится (STDOUT 1, STDERR 2), вместо того, чтобы по умолчанию переходить в STDERR.
Ниже вы можете увидеть, как я запускаю пример dialog
команды с явным указанием того, что вывод должен идти в дескриптор файла 1, что позволяет мне сохранить его в MYVAR.
MYVAR=$(dialog --inputbox "THIS OUTPUT GOES TO FD 1" 25 25 --output-fd 1)
Использование именованных каналов
Альтернативный подход, который имеет большой скрытый потенциал, состоит в том, чтобы использовать то, что называется именованным каналом .
#!/bin/bash
mkfifo /tmp/namedPipe1 # this creates named pipe, aka fifo
# to make sure the shell doesn't hang, we run redirection
# in background, because fifo waits for output to come out
dialog --inputbox "This is an input box with named pipe" 40 40 2> /tmp/namedPipe1 &
# release contents of pipe
OUTPUT="$( cat /tmp/namedPipe1 )"
echo "This is the output " $OUTPUT
# clean up
rm /tmp/namedPipe1
Более подробный обзор ответа user.dz с альтернативным подходом
Оригинальный ответ от user.dz и объяснение ByteCommander о том, что оба обеспечивают хорошее решение и обзор того, что он делает. Тем не менее, я считаю, что более глубокий анализ может быть полезным, чтобы объяснить, почему он работает.
Прежде всего, важно понимать две вещи: какую проблему мы пытаемся решить и каковы основные механизмы механизма оболочки, с которыми мы имеем дело. Задача состоит в том, чтобы захватить вывод команды с помощью подстановки команд. Под упрощенным обзором, который всем известен, подстановки команд фиксируют stdout
команду и позволяют ее использовать другим способом. В этом случае result=$(...)
деталь должна сохранить выходные данные любой команды, обозначенной как, ...
в переменную с именем result
.
Под капотом подстановка команд фактически реализована в виде канала, где есть дочерний процесс (действующая команда, которая выполняется) и процесс чтения (который сохраняет выходные данные в переменную). Это видно по простой трассировке системных вызовов. Обратите внимание, что файловый дескриптор 3 является концом чтения канала, а 4 - концом записи. Для дочернего процесса echo
, который записывает в свой stdout
дескриптор файла 1, этот дескриптор файла фактически является копией дескриптора файла 4, который является концом записи канала. Обратите внимание, что stderr
здесь не играет роли просто потому, что это труба, соединяющая stdout
только.
$ strace -f -e pipe,dup2,write,read bash -c 'v=$(echo "X")'
...
pipe([3, 4]) = 0
strace: Process 6200 attached
[pid 6199] read(3, <unfinished ...>
[pid 6200] dup2(4, 1) = 1
[pid 6200] write(1, "X\n", 2 <unfinished ...>
[pid 6199] <... read resumed> "X\n", 128) = 2
[pid 6200] <... write resumed> ) = 2
[pid 6199] read(3, "", 128) = 0
[pid 6200] +++ exited with 0 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=6200, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
+++ exited with 0 +++
Давайте вернемся к первоначальному ответу на секунду. Так как теперь мы знаем, что dialog
запись поля TUI stdout
, ответ stderr
и внутри подстановки команд stdout
передаются куда-то еще, у нас уже есть часть решения - нам нужно перемонтировать файловые дескрипторы таким образом, чтобы stderr
они передавались в процесс чтения. Это 2>&1
часть ответа. Тем не менее, что мы делаем с TUI box?
Вот где начинается файловый дескриптор 3. dup2()
Системный вызов позволяет нам дублировать файловые дескрипторы, заставляя их эффективно ссылаться на одно и то же место, но мы можем управлять ими по отдельности. Файловые дескрипторы процессов, к которым подключен управляющий терминал, фактически указывают на конкретное терминальное устройство. Это очевидно, если вы делаете
$ ls -l /proc/self/fd
total 0
lrwx------ 1 user1 user1 64 Aug 20 10:30 0 -> /dev/pts/5
lrwx------ 1 user1 user1 64 Aug 20 10:30 1 -> /dev/pts/5
lrwx------ 1 user1 user1 64 Aug 20 10:30 2 -> /dev/pts/5
lr-x------ 1 user1 user1 64 Aug 20 10:30 3 -> /proc/6424/fd
где /dev/pts/5
мое текущее псевдо-терминальное устройство. Таким образом, если мы можем каким-то образом сохранить этот пункт назначения, мы все равно можем записать окно TUI на экран терминала. Вот что exec 3>&1
делает. command > /dev/null
Например, когда вы вызываете команду с перенаправлением , оболочка передает свой дескриптор файла stdout и затем использует dup2()
для записи этого файлового дескриптора /dev/null
. Команда exec
выполняет что-то похожее наdup2()
файловые дескрипторы для всего сеанса оболочки, поэтому любая команда наследует уже перенаправленный файловый дескриптор. То же самое с exec 3>&1
. Файловый дескриптор 3
теперь будет ссылаться на / point на управляющий терминал, и любая команда, которая выполняется в этом сеансе оболочки, будет знать об этом.
Таким образом, когда это result=$(dialog --inputbox test 0 0 2>&1 1>&3);
происходит, оболочка создает канал для записи диалога, но также 2>&1
сначала делает дубликат дескриптора файла команды 2 на дескриптор файла записи этого канала (таким образом, вывод выводится в конец канала чтения в переменную). , в то время как файловый дескриптор 1 будет дублирован на 3. Это сделает файловый дескриптор 1 по-прежнему ссылающимся на управляющий терминал, и на экране появится диалоговое окно TUI.
Теперь, на самом деле есть сокращение для текущего управляющего терминала процесса, который есть /dev/tty
. Таким образом, решение может быть упрощено без использования файловых дескрипторов, просто в:
result=$(dialog --inputbox test 0 0 2>&1 1>/dev/tty);
echo "$result"
Ключевые вещи, которые нужно помнить:
- файловые дескрипторы наследуются от оболочки каждой командой
- Подстановка команд реализована в виде канала
- Дублированные файловые дескрипторы будут ссылаться на то же место, что и исходный, но мы можем манипулировать каждым файловым дескриптором отдельно
Смотрите также
mktemp
команду для создания временного файла.