Використання власних інструментів діалогу: --outout-fd flag
Якщо ви читаєте чоловічу сторінку для діалогу, є опція --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
тепер буде звертатися до / вказувати на керуючий термінал, і будь-яка команда, що працює в цьому сеансі оболонки, буде знати про це.
Отже, коли це 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
команду для створення тимчасового файлу.