Je me suis enseigné moi-même le script bash et j'ai rencontré un problème. J'ai écrit un script pour prendre les entrées de l'utilisateur, en utilisant la commande 'read', et faire de cette entrée une variable à utiliser plus tard dans le script. Le script fonctionne, mais ...
Je voudrais pouvoir l'installer en utilisant 'dialogue'. J'ai découvert que
'dialog --inputbox' dirigera la sortie vers 'stderr' et pour obtenir cette entrée en tant que variable, vous devez la diriger vers un fichier puis la récupérer. Le code que j'ai trouvé pour expliquer cela est:
#!/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
Je vois qu'il envoie le sdterr à /tmp/inputbox.tmp.$$ avec 2>, mais le fichier de sortie ressemble à 'inputbox.tmp.21661'. Lorsque j'essaie de récupérer le fichier, cela me donne une erreur. Je ne suis donc toujours pas en mesure d'obtenir l'entrée utilisateur de la --inputbox en tant que variable.
Exemple de script:
echo " What app would you like to remove? "
read dead_app
sudo apt-get remove --purge $dead_app
Comme vous pouvez le voir, c'est un script de base. Est-il même possible d'obtenir la variable sous forme de mot dialog --inputbox
?
mktemp
commande pour créer un fichier temporaire.Réponses:
^ réponse de @Sneetsher (4 juil.2014 )
Comme demandé, j'essaierai d'expliquer ce que cet extrait de code fait ligne par ligne.
Notez que je vais le simplifier en omettant tous les
;
points - virgules aux extrémités de la ligne, car ils ne sont pas nécessaires si nous écrivons une commande par ligne.E / S - Flux:
Tout d'abord, vous devez comprendre les flux de communication. Il existe 10 flux, numérotés de 0 à 9:
Stream 0 ("STDIN"):
"Entrée standard", le flux d'entrée par défaut pour lire les données du clavier.
Flux 1 ("STDOUT"):
"Sortie standard", le flux de sortie par défaut utilisé pour afficher du texte normal dans le terminal.
Flux 2 ("STDERR"): "Erreur standard", le flux de sortie par défaut utilisé pour afficher les erreurs ou tout autre texte à des fins spéciales dans le terminal.
Streams 3-9: Streams
supplémentaires et librement utilisables. Ils ne sont pas utilisés par défaut et n'existent que lorsque quelque chose tente de les utiliser.
Notez que tous les "flux" sont représentés en interne par des descripteurs de fichiers dans
/dev/fd
(qui est un lien symbolique vers/proc/self/fd
lequel contient un autre lien symbolique pour chaque flux ... c'est un peu compliqué et pas important pour leur comportement, donc je m'arrête ici.). Les flux standard ont également/dev/stdin
,/dev/stdout
et/dev/stderr
(qui sont à nouveau des liens symboliques, etc ...).Le script:
Le Bash intégré
exec
peut être utilisé pour appliquer une redirection de flux au shell, ce qui signifie qu'il affecte toutes les commandes suivantes. Pour plus d'informations, exécutezhelp exec
dans votre terminal.Dans ce cas particulier, le flux 3 est redirigé vers le flux 1 (STDOUT), ce qui signifie que tout ce que nous envoyons au flux 3 plus tard apparaîtra dans notre terminal comme s'il était normalement imprimé sur STDOUT.
Cette ligne se compose de plusieurs parties et structures syntaxiques:
result=$(...)
Cette structure exécute la commande entre crochets et affecte la sortie (STDOUT) à la variable bash
result
. C'est lisible$result
. Tout cela est décrit d'une manière ou d'une autre dans le looong veeeeryman bash
.dialog --inputbox TEXT HEIGHT WIDTH
Cette commande affiche une boîte TUI avec le TEXTE donné, un champ de saisie de texte et deux boutons OK et ANNULER. Si OK est sélectionné, la commande se termine avec le statut 0 et imprime le texte saisi dans STDERR, si CANCEL est sélectionné, il quittera avec le code 1 et n'imprimera rien. Pour plus d'informations, lisez
man dialog
.2>&1 1>&3
Ce sont deux commandes de redirection. Ils seront interprétés de droite à gauche:
1>&3
redirige le flux 1 de la commande (STDOUT) vers le flux personnalisé 3.2>&1
redirige ensuite le flux de commande 2 (STDERR) vers le flux 1 (STDOUT).Cela signifie que tout ce que la commande imprime sur STDOUT apparaît maintenant dans le flux 3, tandis que tout ce qui devait apparaître sur STDERR est maintenant redirigé vers STDOUT.
Ainsi, toute la ligne affiche une invite de texte (sur STDOUT, qui a été redirigé vers le flux 3, que le shell redirige à nouveau vers STDOUT à la fin - voir la
exec 3>&1
commande) et attribue les données entrées (renvoyées via STDERR, puis redirigées vers STDOUT) à la variable Bashresult
.Ce code récupère le code de sortie de la commande précédemment exécutée (ici depuis
dialog
) via la variable Bash réservée$?
(contient toujours le dernier code de sortie) et le stocke simplement dans notre propre variable Bashexitcode
. Il peut être relu$exitcode
. Vous pouvez rechercher plus d'informations à ce sujet dansman bash
, mais cela peut prendre un certain temps ...Le Bash intégré
exec
peut être utilisé pour appliquer une redirection de flux au shell, ce qui signifie qu'il affecte toutes les commandes suivantes. Pour plus d'informations, exécutezhelp exec
dans votre terminal.Dans ce cas particulier, le flux 3 est redirigé vers "flux -", ce qui signifie simplement qu'il doit être fermé. Désormais, les données envoyées au flux 3 ne seront plus redirigées nulle part.
Cette simple
echo
commande (plus d'informations surman echo
) imprime simplement le contenu des deux variables Bashresult
etexitcode
dans STDOUT. Comme nous n'avons plus de redirections de flux explicites ou implicites ici, elles apparaîtront vraiment sur STDOUT et seront donc simplement affichées dans le terminal. Quel miracle! ;-)Sommaire:
Tout d'abord, nous configurons le shell pour rediriger tout ce que nous envoyons vers le flux personnalisé 3 vers STDOUT, afin qu'il apparaisse dans notre terminal.
Ensuite, nous exécutons la
dialog
commande, redirige son STDOUT d'origine vers notre flux personnalisé 3, car il doit être affiché à la fin, mais nous devons temporairement utiliser le flux STDOUT pour autre chose.Nous redirige le STDERR d'origine de la commande, où l'entrée utilisateur de la fenêtre de dialogue est retournée, vers STDOUT par la suite.
Maintenant, nous pouvons capturer le STDOUT (qui contient les données redirigées de STDERR) et le stocker dans notre variable
$result
. Il contient maintenant l'entrée utilisateur souhaitée!Nous voulons également le
dialog
code de sortie de la commande, qui nous indique si OK ou CANCEL a été cliqué. Cette valeur est présentée dans la variable Bash réservée$?
et nous la copions simplement dans notre propre variable$exitcode
.Après cela, nous fermons à nouveau le flux 3, car nous n'en avons plus besoin, pour arrêter d'autres redirections.
Enfin, nous sortons normalement le contenu des deux variables
$result
(l'entrée utilisateur de la fenêtre de dialogue) et$exitcode
(0 pour OK, 1 pour CANCEL) sur le terminal.la source
exec
est inutilement compliquée. Pourquoi ne pas simplement--stdout
choisirdialog
ou rediriger sa sortie par2>&1 >/dev/tty
?Utilisation des propres outils de la boîte de dialogue: --output-fd flag
Si vous lisez la page de manuel pour la boîte de dialogue, il existe une option
--output-fd
qui vous permet de définir explicitement où va la sortie (STDOUT 1, STDERR 2), au lieu d'aller par défaut à STDERR.dialog
Ci-dessous, vous pouvez me voir exécuter un exemple de commande, en précisant explicitement que la sortie doit aller au descripteur de fichier 1, ce qui me permet de l'enregistrer dans MYVAR.MYVAR=$(dialog --inputbox "THIS OUTPUT GOES TO FD 1" 25 25 --output-fd 1)
Utilisation de canaux nommés
Une approche alternative qui a beaucoup de potentiel caché consiste à utiliser quelque chose appelé pipe nommée .
Un aperçu plus approfondi de la réponse de user.dz avec une approche alternative
La réponse originale de user.dz et l'explication de ByteCommander à ce sujet fournissent toutes deux une bonne solution et un aperçu de ce qu'il fait. Cependant, je crois qu'une analyse plus approfondie pourrait être bénéfique pour expliquer pourquoi cela fonctionne.
Tout d'abord, il est important de comprendre deux choses: quel est le problème que nous essayons de résoudre et quels sont les rouages sous-jacents des mécanismes shell avec lesquels nous avons affaire. La tâche consiste à capturer la sortie d'une commande via la substitution de commande. Sous une vue simpliste que tout le monde connaît, les substitutions de commande capturent la
stdout
commande et la laissent être réutilisée par autre chose. Dans ce cas, laresult=$(...)
partie doit enregistrer la sortie de la commande désignée par...
dans une variable appeléeresult
.Sous le capot, la substitution de commandes est en fait implémentée sous forme de canal, où il existe un processus enfant (la commande réelle qui s'exécute) et un processus de lecture (qui enregistre la sortie dans une variable). Cela est évident avec une simple trace d'appels système. Notez que le descripteur de fichier 3 est l'extrémité de lecture du canal, tandis que 4 est l'extrémité d'écriture. Pour le processus enfant de
echo
, qui écrit dans sonstdout
- le descripteur de fichier 1, ce descripteur de fichier est en fait une copie du descripteur de fichier 4, qui est la fin d'écriture du canal. Notez que celastderr
ne joue pas de rôle ici, simplement parce que c'est un tuyau qui se connectestdout
uniquement.Revenons à la réponse originale pendant une seconde. Puisque nous savons maintenant que l'
dialog
écriture de la zone TUIstdout
, la réponsestderr
et la substitution de commandesstdout
sont canalisées ailleurs, nous avons déjà une partie de la solution - nous devons recâbler les descripteurs de fichiers de manière à cestderr
qu'ils soient canalisés vers le processus de lecture. C'est la2>&1
partie de la réponse. Cependant, que faisons-nous avec la boîte TUI?C'est là que le descripteur de fichier 3 entre en jeu. L'
dup2()
appel système nous permet de dupliquer les descripteurs de fichier, en les faisant effectivement référence au même endroit, mais nous pouvons les manipuler séparément. Les descripteurs de fichiers des processus auxquels un terminal de contrôle est connecté pointent en fait vers un périphérique terminal spécifique. Cela est évident si vous le faitesoù
/dev/pts/5
est mon pseudo-terminal actuel. Ainsi, si nous pouvons en quelque sorte enregistrer cette destination, nous pouvons toujours écrire la boîte TUI sur l'écran du terminal. Voilà ce quiexec 3>&1
fait. Lorsque vous appelez une commande avec redirectioncommand > /dev/null
par exemple, le shell transmet son descripteur de fichier stdout puis l'utilisedup2()
pour écrire ce descripteur de fichier/dev/null
. Laexec
commande exécute quelque chose de similaire auxdup2()
descripteurs de fichiers pour toute la session shell, ce qui fait que toute commande hérite d'un descripteur de fichier déjà redirigé. Même chose avecexec 3>&1
. Le descripteur de fichier3
fera désormais référence à / pointera vers le terminal de contrôle, et toute commande exécutée dans cette session shell le saura.Donc, quand cela
result=$(dialog --inputbox test 0 0 2>&1 1>&3);
se produit, le shell crée un tube pour que la boîte de dialogue écrive, mais aussi2>&1
fera d'abord dupliquer le descripteur de fichier de la commande 2 sur le descripteur de fichier d'écriture de ce tube (faisant ainsi sortir la sortie à la fin du tube et dans la variable) , tandis que le descripteur de fichier 1 sera dupliqué sur 3. Cela fera que le descripteur de fichier 1 se référera toujours au terminal de contrôle, et la boîte de dialogue TUI s'affichera à l'écran.Maintenant, il y a en fait un raccourci pour le terminal de contrôle actuel du processus, qui est
/dev/tty
. Ainsi, la solution peut être simplifiée sans utiliser de descripteurs de fichiers, simplement en:Points clés à retenir:
Voir également
la source
--stdout
option peut être dangereuse et échoue facilement sur certains systèmes, et je pense que--output-fd 1
c'est la même chose:--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.
- Cependant, l'idée de pipe nommée est cool!--output-fd
, ce qui est l'option que j'ai utilisée ici, non--stdout
. Deuxièmement, la boîte de dialogue est dessinée en premier sur stdout, la sortie renvoyée est la deuxième. Nous ne faisons pas ces deux choses en même temps. Cependant,--output-fd
n'exige pas spécifiquement que l'on utilise fd 1 (STDOUT). Il peut facilement être redirigé vers un autre descripteur de fichier: DI ne peut pas l'expliquer !!! Si vous pouvez comprendre ce qu'ils disent dans la référence: Advanced Bash-Scripting Guide: Chapter 20. Redirection d'E / S , écrivez une nouvelle réponse et je vous donnerai 50repUne prime a été donnée, pour des explications, voir la réponse de ByteCommander . :) Ceci fait partie de l'histoire.
La source: boîte de dialogue dans bash n'extrait pas correctement les variables
Référence: Guide avancé de script de Bash: Chapitre 20. Redirection d'E / S
la source
Cela fonctionne pour moi:
La page de manuel de
dialog
raconte --stdout:Quelqu'un peut-il dire dans quelle plate-forme ou environnement cela ne fonctionne pas? La redirection de la
dialog
sortie vers2>&1 >/dev/tty
fonctionne-t-elle mieux alors?la source
Dans le cas où quelqu'un d'autre a atterri ici de Google et bien que cette question demande spécifiquement bash, voici une autre alternative:
Vous pouvez utiliser la zénité . Zenity est un utilitaire graphique qui peut être utilisé dans des scripts bash. Mais bien sûr, cela nécessiterait un serveur X, comme l'a souligné à juste titre user877329.
Puis dans votre script:
Lien utile .
la source
dialog
. C'est comme si je venais vous demander "Comment puis-je écrire ceci et cela en python?", Mais vous me donnez bash - je suis très heureux que cela puisse être fait de manière différente, mais ce n'est pas ce que je demandeLa réponse fournie par Sneetsher est un peu plus élégante, mais je peux expliquer ce qui ne va pas: la valeur de
$$
est différente à l'intérieur des astuces (car elle démarre un nouveau shell et$$
est le PID du shell actuel). Vous voudrez mettre le nom du fichier dans une variable, puis vous référer à cette variable partout à la place.Dans ce cas, éviter le fichier temporaire serait une meilleure solution, mais il y aura de nombreuses situations où vous ne pourrez pas éviter un fichier temporaire.
la source