İletişim kutusunun kendi araçlarını kullanma: --output-fd flag
İletişim için man sayfasını okursanız --output-fd
, varsayılan olarak STDERR'a gitmek yerine çıktının nereye gittiğini (STDOUT 1, STDERR 2) açıkça ayarlamanıza izin veren bir seçenek vardır .
Aşağıdaki örnekte, dialog
komutun MYVAR'a kaydetmeme izin veren dosya tanımlayıcı 1'e gitmesi gerektiğini açıkça belirten örnek komutu çalıştırdığımı görebilirsiniz .
MYVAR=$(dialog --inputbox "THIS OUTPUT GOES TO FD 1" 25 25 --output-fd 1)
Adlandırılmış yöneltmeleri kullanma
Çok fazla gizli potansiyele sahip alternatif yaklaşım, adlandırılmış boru olarak bilinen bir şey kullanmaktır .
#!/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 ve ByteCommander'ın bu konudaki açıklaması , hem iyi bir çözüm hem de ne yaptığına genel bir bakış sağlar. Ancak, daha derin bir analizin neden işe yaradığını açıklamak için faydalı olabileceğine inanıyorum .
Her şeyden önce, iki şeyi anlamak önemlidir: çözmeye çalıştığımız sorun nedir ve uğraştığımız kabuk mekanizmalarının altında yatan çalışmalar nelerdir. Görev, bir komutun çıkışını komut değiştirme yoluyla yakalamaktır. Herkesin bildiği basit bir bakış altında, komut ikameleri stdout
bir komutu yakalar ve başka bir şey tarafından yeniden kullanılmasına izin verir. Bu durumda, result=$(...)
parça, belirtilen komutun çıktısını ...
adlı bir değişkene kaydetmelidir result
.
Kaputun altında, komut ikamesi aslında bir alt işlem (çalışan gerçek komut) ve okuma işlemi (çıktıyı değişkene kaydeder) olan boru olarak uygulanır. Bu, sistem çağrılarının basit bir izlemesiyle belirgindir. Dosya tanımlayıcı 3'ün borunun okuma ucu, 4'ün ise yazma sonu olduğuna dikkat edin. Dosya tanımlayıcısı 1'e echo
yazılan alt işlemi için stdout
bu dosya tanımlayıcısı, aslında borunun yazma sonu olan dosya tanımlayıcı 4'ün kopyasıdır. stderr
Burada rol oynamadığına dikkat edin , çünkü sadece bir boru bağlantısı 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 +++
Bir saniye orijinal cevaba geri dönelim. Artık dialog
TUI kutusunu yazar stdout
, yanıtlar stderr
ve komut ikamesi içinde stdout
başka bir yere pipetlendiğini bildiğimizden beri, çözümün bir parçası zaten var - dosya tanımlayıcılarını stderr
okuyucu sürecine bağlanacak şekilde yeniden sarmamız gerekiyor. Bu 2>&1
cevabın bir parçası. Ancak TUI kutusu ile ne yapıyoruz?
Burada dosya tanımlayıcı 3 dup2()
devreye girer. Syscall, dosya tanımlayıcıları çoğaltmamıza izin verir, bu da onları etkili bir şekilde aynı yere yönlendirir, ancak bunları ayrı olarak manipüle edebiliriz. Kontrol terminali bağlı işlemlerin dosya tanımlayıcıları aslında belirli terminal cihazını gösterir. Bunu yaparsanız bu açıktır
$ 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
şu anki sahte terminal cihazım nerede . Böylece, bir şekilde bu hedefi kaydedebilirsek, yine de TUI kutusunu terminal ekranına yazabiliriz. İşte exec 3>&1
böyle. command > /dev/null
Örneğin, yeniden yönlendirme ile bir komut çağırdığınızda , kabuk stdout dosya tanımlayıcısını geçirir ve daha sonra dup2()
bu dosya tanımlayıcısını yazmak için kullanır /dev/null
. exec
Komut gerçekleştirdiği benzer bir şeydup2()
bütün kabuk oturumu için dosya tanıtıcı, böylece herhangi bir komut devralır zaten yönlendirildi dosya tanımlayıcısı yapma. İle aynı exec 3>&1
. Dosya tanımlayıcı 3
şimdi kontrol eden terminale başvuracak / işaret edecek ve bu kabuk oturumunda çalışan herhangi bir komut onu tanıyacaktır.
Bu result=$(dialog --inputbox test 0 0 2>&1 1>&3);
durumda, kabuk iletişim kutusunun yazılması için bir boru oluşturur, ancak 2>&1
önce komutun dosya tanımlayıcısını 2 o borunun yazma dosyası tanımlayıcısına çoğaltır (böylece çıktının borunun sonuna ve değişkene gitmesini sağlar) , dosya tanımlayıcı 1 3 üzerine çoğaltılır. Bu, dosya tanımlayıcı 1'in hala kontrol terminaline başvurmasını sağlar ve TUI iletişim kutusu ekranda görünecektir.
Şimdi, aslında sürecin akım kontrol terminali için bir kısa el var /dev/tty
. Böylece, çözüm, dosya tanımlayıcıları kullanılmadan basitçe basitleştirilebilir:
result=$(dialog --inputbox test 0 0 2>&1 1>/dev/tty);
echo "$result"
Hatırlanması gereken önemli noktalar:
- dosya tanımlayıcıları her komut tarafından kabuktan devralınır
- komut ikamesi boru olarak uygulanır
- çoğaltılmış dosya tanımlayıcıları orijinaliyle aynı yere atıfta bulunacaktır, ancak her dosya tanımlayıcısını ayrı olarak değiştirebiliriz
Ayrıca bakınız
mktemp
geçici bir dosya oluşturmak için command komutunu kullanabilirsiniz .