Menggunakan alat dialog sendiri: --output-fd flag
Jika Anda membaca halaman manual untuk dialog, ada opsi --output-fd
, yang memungkinkan Anda untuk secara eksplisit mengatur di mana output berjalan (STDOUT 1, STDERR 2), alih-alih secara default pergi ke STDERR.
Di bawah ini Anda dapat melihat saya menjalankan dialog
perintah sampel , dengan secara eksplisit menyatakan bahwa output harus masuk ke file descriptor 1, yang memungkinkan saya untuk menyimpannya ke MYVAR.
MYVAR=$(dialog --inputbox "THIS OUTPUT GOES TO FD 1" 25 25 --output-fd 1)
Menggunakan pipa bernama
Pendekatan alternatif yang memiliki banyak potensi tersembunyi, adalah dengan menggunakan sesuatu yang dikenal sebagai pipa bernama .
#!/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
Tinjauan yang lebih mendalam tentang jawaban user.dz dengan pendekatan alternatif
Jawaban asli oleh user.dz dan penjelasan ByteCommander tentang keduanya memberikan solusi dan ikhtisar yang baik tentang apa yang dilakukannya. Namun, saya percaya analisis yang lebih dalam bisa bermanfaat untuk menjelaskan mengapa itu berhasil.
Pertama-tama, penting untuk memahami dua hal: apa masalah yang kita coba selesaikan dan apa yang menjadi dasar mekanisme shell yang kita hadapi. Tugasnya adalah untuk menangkap output dari suatu perintah melalui substitusi perintah. Di bawah gambaran sederhana yang diketahui semua orang, pergantian perintah menangkap stdout
perintah dan membiarkannya digunakan kembali oleh sesuatu yang lain. Dalam hal ini, result=$(...)
bagian harus menyimpan output dari perintah apa pun yang ditunjuk oleh ...
menjadi variabel yang dipanggil result
.
Di bawah kap, substitusi perintah sebenarnya diimplementasikan sebagai pipa, di mana ada proses anak (perintah aktual yang berjalan) dan proses membaca (yang menyimpan output ke variabel). Ini terbukti dengan jejak sederhana dari panggilan sistem. Perhatikan bahwa file deskriptor 3 adalah ujung baca dari pipa, sedangkan 4 adalah akhir tulis. Untuk proses anak echo
, yang menulis ke stdout
- file deskriptor 1, file deskriptor tersebut sebenarnya adalah salinan file deskriptor 4, yang merupakan akhir penulisan pipa. Perhatikan bahwa stderr
ini tidak berperan di sini, hanya karena itu hanya pipa penghubung 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 +++
Mari kita kembali ke jawaban semula. Karena sekarang kita tahu bahwa dialog
menulis kotak TUI ke stdout
, menjawab stderr
, dan dalam substitusi perintah stdout
disalurkan ke tempat lain, kita sudah memiliki bagian dari solusi - kita perlu me-rewire deskriptor file sedemikian rupa yang stderr
akan disalurkan ke proses pembaca. Ini adalah2>&1
bagian dari jawabannya. Namun, apa yang kita lakukan dengan kotak TUI?
Di situlah file deskriptor 3 masuk. dup2()
Syscall memungkinkan kita untuk menduplikasi deskriptor file, membuatnya secara efektif merujuk ke tempat yang sama, namun kita dapat memanipulasinya secara terpisah. File deskriptor dari proses yang memiliki terminal kontrol terpasang sebenarnya menunjuk ke perangkat terminal tertentu. Ini terbukti jika Anda melakukannya
$ 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
di mana /dev/pts/5
perangkat pseudo-terminal saya saat ini. Jadi, jika kita dapat menyimpan tujuan ini, kita masih dapat menulis kotak TUI ke layar terminal. Itu yang exec 3>&1
dilakukannya. Ketika Anda memanggil perintah dengan pengalihan command > /dev/null
misalnya, shell melewati itu deskriptor file stdout dan kemudian menggunakan dup2()
untuk menulis file deskriptor itu /dev/null
. The exec
Perintah Melakukan sesuatu yang mirip dengandup2()
file descriptor untuk sesi shell utuh, sehingga membuat perintah mewarisi sudah diarahkan file descriptor. Sama dengan exec 3>&1
. Deskriptor file 3
sekarang akan merujuk ke / menunjuk ke terminal pengendali, dan perintah apa pun yang berjalan di sesi shell akan mengetahuinya.
Jadi ketika result=$(dialog --inputbox test 0 0 2>&1 1>&3);
terjadi, shell membuat pipa untuk dialog untuk menulis, tetapi juga2>&1
pertama-tama akan membuat file descriptor 2 perintah diduplikasi ke deskriptor file tulis dari pipa itu (sehingga membuat output pergi ke ujung baca pipa dan ke dalam variabel) , sementara file deskriptor 1 akan digandakan ke 3. Ini akan membuat file deskriptor 1 masih merujuk ke terminal pengendali, dan dialog TUI akan muncul di layar.
Sekarang, sebenarnya ada jalan pintas untuk terminal pengontrol saat ini dari proses, yaitu /dev/tty
. Dengan demikian, solusinya dapat disederhanakan tanpa menggunakan deskriptor file, cukup menjadi:
result=$(dialog --inputbox test 0 0 2>&1 1>/dev/tty);
echo "$result"
Hal-hal penting untuk diingat:
- deskriptor file diwarisi dari shell oleh setiap perintah
- substitusi perintah diimplementasikan sebagai pipa
- deskriptor file yang digandakan akan merujuk ke tempat yang sama dengan yang asli, tetapi kami dapat memanipulasi setiap deskriptor file secara terpisah
Lihat juga
mktemp
perintah untuk membuat file sementara.