ダイアログ独自のツールの使用:--output-fdフラグ
ダイアログのmanページを読むと、オプション--output-fd
があり、デフォルトでSTDERRに行く代わりに、出力の行き先(STDOUT 1、STDERR 2)を明示的に設定できます。
以下に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による元の回答とそれに関するByteCommanderの説明はどちらも、優れたソリューションとその概要を提供します。ただし、より詳細な分析は、なぜ機能するのかを説明するのに有益であると考えています。
まず、2つのことを理解することが重要です:解決しようとしている問題は何か、対処しているシェルメカニズムの基本的な動作は何か。タスクは、コマンド置換を介してコマンドの出力をキャプチャすることです。誰もが知っている単純化された概要の下で、コマンド置換はコマンドをキャプチャし、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ボックスで何をするのでしょうか?
そこでファイル記述子3をdup2()
使用します。syscallを使用すると、ファイル記述子を複製して、同じ場所を効果的に参照できますが、別々に操作できます。制御端末が接続されているプロセスのファイル記述子は、実際には特定の端末デバイスを指します。あなたがする場合、これは明らかです
$ 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
コマンドを使用して一時ファイルを作成することもできます。