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 .
#!/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
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, la result=$(...)
partie doit enregistrer la sortie de la commande désignée par ...
dans une variable appelée result
.
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 son stdout
- 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 cela stderr
ne joue pas de rôle ici, simplement parce que c'est un tuyau qui se connecte stdout
uniquement.
$ 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 +++
Revenons à la réponse originale pendant une seconde. Puisque nous savons maintenant que l' dialog
écriture de la zone TUI stdout
, la réponse stderr
et la substitution de commandes stdout
sont canalisées ailleurs, nous avons déjà une partie de la solution - nous devons recâbler les descripteurs de fichiers de manière à ce stderr
qu'ils soient canalisés vers le processus de lecture. C'est la 2>&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 faites
$ 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
où /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 qui exec 3>&1
fait. Lorsque vous appelez une commande avec redirection command > /dev/null
par exemple, le shell transmet son descripteur de fichier stdout puis l'utilise dup2()
pour écrire ce descripteur de fichier /dev/null
. La exec
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 avec exec 3>&1
. Le descripteur de fichier 3
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 aussi 2>&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:
result=$(dialog --inputbox test 0 0 2>&1 1>/dev/tty);
echo "$result"
Points clés à retenir:
- les descripteurs de fichiers sont hérités du shell par chaque commande
- la substitution de commande est implémentée en tant que pipe
- les descripteurs de fichiers dupliqués feront référence au même endroit que l'original, mais nous pouvons manipuler chaque descripteur de fichier séparément
Voir également
mktemp
commande pour créer un fichier temporaire.