Я учил себя скриптовому Bash и столкнулся с проблемой. Я написал скрипт для получения ввода от пользователя с помощью команды read и превращения этого ввода в переменную для последующего использования в скрипте. Скрипт работает, но ....
Я хотел бы иметь возможность настроить его с помощью «диалога». я узнал что
'dialog --inputbox' направит вывод в 'stderr', и чтобы получить этот ввод как переменную, вы должны направить его в файл и затем извлечь его. Код, который я нашел, чтобы объяснить это:
#!/bin/bash
dialog --inputbox \
"What is your username?" 0 0 2> /tmp/inputbox.tmp.$$
retval=$?
input=`cat /tmp/inputbox.tmp.$$`
rm -f /tmp/inputbox.tmp.$$
case $retval in
0)
echo "Your username is '$input'";;
1)
echo "Cancel pressed.";;
esac
Я вижу, что он отправляет sdterr в /tmp/inputbox.tmp.$$ с 2>, но выходной файл выглядит как 'inputbox.tmp.21661'. Когда я пытаюсь просмотреть файл, появляется сообщение об ошибке. Поэтому я все еще не могу получить пользовательский ввод из --inputbox как переменную.
Пример скрипта:
echo " What app would you like to remove? "
read dead_app
sudo apt-get remove --purge $dead_app
Итак, как вы можете видеть, это основной сценарий. Можно ли вообще получить переменную как слово dialog --inputbox
?
mktemp
команду для создания временного файла.Ответы:
^ ответ от @Sneetsher (4 июля 2014)
По запросу я постараюсь объяснить, что этот фрагмент делает построчно.
Обратите внимание, что я упросту это, опустив все
;
точки с запятой в конце строки, потому что они не нужны, если мы пишем одну команду на строку.I / O - потоки:
Во-первых, вам необходимо понять коммуникационные потоки. Есть 10 потоков, пронумерованных от 0 до 9:
Поток 0 («STDIN»):
«Стандартный ввод», поток ввода по умолчанию для чтения данных с клавиатуры.
Поток 1 («STDOUT»):
«Стандартный вывод», поток вывода по умолчанию, используемый для отображения обычного текста в терминале.
Поток 2 («STDERR»): «Стандартная ошибка», выходной поток по умолчанию, используемый для отображения ошибок или другого текста для специальных целей в терминале.
Потоки 3-9:
дополнительные, свободно используемые потоки. Они не используются по умолчанию и не существуют, пока что-то не попытается их использовать.
Обратите внимание, что все «потоки» внутренне представлены дескрипторами файлов в
/dev/fd
(это символическая ссылка,/proc/self/fd
которая содержит другую символическую ссылку для каждого потока ... это немного сложно и не важно для их поведения, поэтому я остановлюсь здесь). Стандартные потоки также/dev/stdin
,/dev/stdout
и/dev/stderr
(что символические ссылки снова, и т.д ...).Сценарий:
Встроенный Bash
exec
может быть использован для применения перенаправления потока к оболочке, что означает, что он влияет на все следующие команды. Для получения дополнительной информации, запуститеhelp exec
в своем терминале.В этом особом случае поток 3 перенаправляется в поток 1 (STDOUT), что означает, что все, что мы отправим в поток 3 позже, будет отображаться в нашем терминале, как если бы оно было нормально напечатано в STDOUT.
Эта строка состоит из множества частей и синтаксических структур:
result=$(...)
Эта структура выполняет команду в скобках и назначает вывод (STDOUT) переменной bash
result
. Это читабельно до конца$result
. Все это описано как-то в самом простонародьеman bash
.dialog --inputbox TEXT HEIGHT WIDTH
Эта команда показывает окно TUI с заданным ТЕКСТОМ, полем ввода текста и двумя кнопками ОК и ОТМЕНА. Если выбрано ОК, команда завершается со статусом 0 и печатает введенный текст в STDERR, если выбирается ОТМЕНА, она завершается с кодом 1 и ничего не печатает. Для получения дополнительной информации читайте
man dialog
.2>&1 1>&3
Это две команды перенаправления. Они будут интерпретироваться справа налево:
1>&3
перенаправляет поток команды 1 (STDOUT) в пользовательский поток 3.2>&1
затем перенаправляет поток 2 команды (STDERR) в поток 1 (STDOUT).Это означает, что все, что команда выводит на STDOUT, теперь появляется в потоке 3, а все, что было предназначено для отображения на STDERR, теперь перенаправляется на STDOUT.
Таким образом, вся строка отображает текстовое приглашение (в STDOUT, которое было перенаправлено в поток 3, который оболочка снова перенаправляет обратно в STDOUT в конце - см.
exec 3>&1
Команду) и назначает введенные данные (возвращенные через STDERR, затем перенаправленные в STDOUT) в переменную Bashresult
.Этот код извлекает код завершения ранее выполненной команды (здесь
dialog
) из зарезервированной переменной Bash$?
(всегда содержит последний код завершения) и просто сохраняет его в нашей собственной переменной Bashexitcode
. Это можно прочитать$exitcode
снова. Вы можете найти более подробную информацию об этомman bash
, но это может занять некоторое время ...Встроенный Bash
exec
может быть использован для применения перенаправления потока к оболочке, что означает, что он влияет на все следующие команды. Для получения дополнительной информации, запуститеhelp exec
в своем терминале.В этом особом случае поток 3 перенаправляется на «поток -», что означает, что он должен быть закрыт. Данные, отправленные в поток 3, больше нигде не будут перенаправлены.
Эта простая
echo
команда (больше информацииman echo
) просто печатает содержимое двух переменных Bashresult
иexitcode
в STDOUT. Поскольку у нас больше нет явных или неявных перенаправлений потоков, они действительно будут появляться в STDOUT и поэтому просто будут отображаться в терминале. Какое чудо! ;-)Резюме:
Сначала мы устанавливаем оболочку, чтобы перенаправить все, что мы посылаем, в пользовательский поток 3 обратно в STDOUT, чтобы он отображался в нашем терминале.
Затем мы запускаем
dialog
команду, перенаправляем ее исходный STDOUT в наш пользовательский поток 3, потому что он должен отображаться в конце, но нам временно нужно использовать поток STDOUT для чего-то еще.Мы перенаправляем исходный STDERR команды, где возвращается пользовательский ввод диалогового окна, в STDOUT впоследствии.
Теперь мы можем захватить STDOUT (который содержит перенаправленные данные из STDERR) и сохранить его в нашей переменной
$result
. Он содержит желаемый пользовательский ввод сейчас!Нам также нужен
dialog
код завершения команды, который показывает нам, был ли нажат OK или CANCEL. Это значение представлено в зарезервированной переменной Bash,$?
и мы просто копируем его в нашу собственную переменную$exitcode
.После этого мы снова закрываем поток 3, так как он нам больше не нужен, чтобы остановить дальнейшие перенаправления.
Наконец, мы обычно выводим содержимое обеих переменных
$result
(пользовательский ввод диалогового окна) и$exitcode
(0 для ОК, 1 для ОТМЕНА) на терминал.источник
exec
излишне сложно. Почему бы просто не использовать--stdout
опциюdialog
или не перенаправить вывод2>&1 >/dev/tty
?Использование собственных инструментов диалога: --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)
Использование именованных каналов
Альтернативный подход, который имеет большой скрытый потенциал, состоит в том, чтобы использовать то, что называется именованным каналом .
Более подробный обзор ответа user.dz с альтернативным подходом
Оригинальный ответ от user.dz и объяснение ByteCommander о том, что оба обеспечивают хорошее решение и обзор того, что он делает. Тем не менее, я считаю, что более глубокий анализ может быть полезным, чтобы объяснить, почему он работает.
Прежде всего, важно понимать две вещи: какую проблему мы пытаемся решить и каковы основные механизмы механизма оболочки, с которыми мы имеем дело. Задача состоит в том, чтобы захватить вывод команды с помощью подстановки команд. Под упрощенным обзором, который всем известен, подстановки команд фиксируют
stdout
команду и позволяют ее использовать другим способом. В этом случаеresult=$(...)
деталь должна сохранить выходные данные любой команды, обозначенной как,...
в переменную с именемresult
.Под капотом подстановка команд фактически реализована в виде канала, где есть дочерний процесс (действующая команда, которая выполняется) и процесс чтения (который сохраняет выходные данные в переменную). Это видно по простой трассировке системных вызовов. Обратите внимание, что файловый дескриптор 3 является концом чтения канала, а 4 - концом записи. Для дочернего процесса
echo
, который записывает в свойstdout
дескриптор файла 1, этот дескриптор файла фактически является копией дескриптора файла 4, который является концом записи канала. Обратите внимание, чтоstderr
здесь не играет роли просто потому, что это труба, соединяющаяstdout
только.Давайте вернемся к первоначальному ответу на секунду. Так как теперь мы знаем, что
dialog
запись поля TUIstdout
, ответstderr
и внутри подстановки командstdout
передаются куда-то еще, у нас уже есть часть решения - нам нужно перемонтировать файловые дескрипторы таким образом, чтобыstderr
они передавались в процесс чтения. Это2>&1
часть ответа. Тем не менее, что мы делаем с TUI box?Вот где начинается файловый дескриптор 3.
dup2()
Системный вызов позволяет нам дублировать файловые дескрипторы, заставляя их эффективно ссылаться на одно и то же место, но мы можем управлять ими по отдельности. Файловые дескрипторы процессов, к которым подключен управляющий терминал, фактически указывают на конкретное терминальное устройство. Это очевидно, если вы делаетегде
/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
. Таким образом, решение может быть упрощено без использования файловых дескрипторов, просто в:Ключевые вещи, которые нужно помнить:
Смотрите также
источник
--stdout
опция может быть опасной и в некоторых системах она не работает, и я думаю, что--output-fd 1
делает то же самое:--stdout: Direct output to the standard output. This option is provided for compatibility with Xdialog, however using it in portable scripts is not recommended, since curses normally writes its screen updates to the standard output. If you use this option, dialog attempts to reopen the terminal so it can write to the display. Depending on the platform and your environment, that may fail.
- Однако идея с именованным каналом - это круто!--output-fd
, какой вариант я здесь использовал, а не--stdout
. Во-вторых, диалог выводится на стандартный вывод первым, возвращаемый результат - вторым. Мы не делаем эти две вещи одновременно. Тем--output-fd
не менее, специально не требуется использовать fd 1 (STDOUT). Его можно легко перенаправить на другой файловый дескриптор: D Я не могу это объяснить !!! Если вы понимаете, о чем они говорят в справочнике: Расширенное руководство по написанию сценариев: Глава 20. Перенаправление ввода / вывода , напишите новый ответ, и я дам вам 50rep.Щедрость была дана, для объяснения см . Ответ ByteCommander . :) Это часть истории.
Источник: Диалог в bash неправильно захватывает переменные.
Ссылка: Руководство по расширенному написанию сценариев Bash: Глава 20. Перенаправление ввода / вывода
источник
Это работает для меня:
Страница руководства
dialog
рассказывает о --stdout:Кто-нибудь может сказать, в какой платформе или среде это не работает? Перенаправление
dialog
вывода2>&1 >/dev/tty
вместо этого работает лучше, чем?источник
В случае, если кто-то еще тоже попал сюда из Google, и хотя этот вопрос задается специально для bash, здесь есть другая альтернатива:
Вы можете использовать zenity . Zenity - графическая утилита, которую можно использовать внутри скриптов bash. Но, конечно, для этого потребуется X-сервер, как справедливо указал user877329.
Тогда в вашем сценарии:
Полезная ссылка .
источник
dialog
. Как будто я прихожу и спрашиваю вас: «Как мне написать это и то, что на питоне?», Но вы даете мне удар - я очень рад, что это можно сделать по-другому, но я не об этом спрашиваюОтвет, предоставленный Sneetsher, несколько более элегантен, но я могу объяснить, что не так: значение
$$
внутри обратных тэков отличается (потому что он запускает новую оболочку и$$
является PID текущей оболочки). Вы захотите поместить имя файла в переменную, а затем ссылаться на эту переменную.В этом случае было бы лучше избежать временного файла, но будет много ситуаций, когда вы не сможете избежать временного файла.
источник