Korzystanie z własnych narzędzi okna dialogowego: --output-fd flag
Jeśli czytasz stronę --output-fd
podręcznika dla okna dialogowego, dostępna jest opcja , która pozwala ci jawnie określić, dokąd idzie wyjście (STDOUT 1, STDERR 2), zamiast domyślnie przechodzić do STDERR.
Poniżej możesz zobaczyć, jak uruchamiam przykładowe dialog
polecenie, z wyraźnym stwierdzeniem, że dane wyjściowe muszą przejść do deskryptora pliku 1, co pozwala mi zapisać je w MYVAR.
MYVAR=$(dialog --inputbox "THIS OUTPUT GOES TO FD 1" 25 25 --output-fd 1)
Używanie nazwanych potoków
Alternatywnym podejściem, które ma wiele ukrytego potencjału, jest użycie czegoś zwanego potokiem nazwanym .
#!/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
Bardziej szczegółowy przegląd odpowiedzi user.dz z alternatywnym podejściem
Oryginalna odpowiedź user.dz i wyjaśnienie ByteCommander, że oba stanowią dobre rozwiązanie i przegląd tego, co robi. Uważam jednak, że głębsza analiza może być korzystna dla wyjaśnienia, dlaczego to działa.
Przede wszystkim ważne jest zrozumienie dwóch rzeczy: jaki jest problem, który próbujemy rozwiązać i jakie są podstawowe mechanizmy mechanizmów powłokowych, z którymi mamy do czynienia. Zadanie polega na przechwytywaniu danych wyjściowych polecenia poprzez podstawienie polecenia. W uproszczonym przeglądzie, który wszyscy znają, podstawienia poleceń przechwytują stdout
polecenie i pozwalają na jego ponowne użycie przez coś innego. W takim przypadku result=$(...)
część powinna zapisać wynik dowolnego polecenia wyznaczonego przez ...
w zmiennej o nazwie result
.
Pod maską zastępowanie poleceń jest faktycznie realizowane jako potok, w którym znajduje się proces potomny (aktualne polecenie, które się uruchamia) i proces odczytu (który zapisuje dane wyjściowe do zmiennej). Widać to po prostym śledzeniu wywołań systemowych. Zauważ, że deskryptor pliku 3 to koniec odczytu potoku, a 4 to koniec zapisu. W przypadku procesu potomnego echo
, który zapisuje do swojego stdout
- deskryptora pliku 1, ten deskryptor pliku jest w rzeczywistości kopią deskryptora pliku 4, który jest końcem zapisu potoku. Zauważ, że stderr
tutaj nie gra żadnej roli, po prostu dlatego, że to stdout
tylko połączenie rur .
$ 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 +++
Wróćmy na chwilę do pierwotnej odpowiedzi. Odkąd wiemy, że dialog
zapisuje pole TUI do stdout
, odpowiedź stderr
i w ramach zastępowania komend stdout
zostaje potokowany gdzieś indziej, mamy już część rozwiązania - musimy ponownie połączyć deskryptory plików w taki sposób, stderr
aby zostały przesłane do procesu czytnika. To jest 2>&1
część odpowiedzi. Co jednak robimy z pudełkiem TUI?
Właśnie tam pojawia się deskryptor pliku 3. dup2()
Syscall pozwala nam powielać deskryptory plików, dzięki czemu skutecznie odnoszą się do tego samego miejsca, ale możemy nimi manipulować osobno. Deskryptory plików procesów, do których przyłączony jest terminal sterujący, wskazują konkretny terminal. Jest to oczywiste, jeśli tak
$ 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
gdzie /dev/pts/5
jest moje obecne urządzenie pseudo-końcowe. Tak więc, jeśli uda nam się w jakiś sposób zapisać to miejsce docelowe, nadal możemy zapisać pole TUI na ekranie terminala. Właśnie to exec 3>&1
robi. Gdy command > /dev/null
na przykład wywołujesz polecenie z przekierowaniem , powłoka przekazuje swój deskryptor standardowego pliku, a następnie używa dup2()
do zapisania tego deskryptora pliku /dev/null
. exec
Wykonuje polecenia coś podobnego dodup2()
deskryptorów dla całej sesji powłoki, dzięki czemu żadnemu Dziedzicz komenda już przekierowany deskryptor pliku. To samo z exec 3>&1
. Deskryptor pliku 3
będzie teraz odnosił się do / point do kontrolującego terminala, a każde polecenie uruchomione w tej sesji powłoki będzie o tym wiedzieć.
Kiedy więc to result=$(dialog --inputbox test 0 0 2>&1 1>&3);
nastąpi, powłoka tworzy potok do okna dialogowego do zapisu, ale 2>&1
najpierw spowoduje, że deskryptor pliku polecenia 2 zostanie zduplikowany na deskryptorze pliku zapisu tej potoku (w ten sposób wyjście zostanie przesłane do końca odczytu potoku i do zmiennej) , podczas gdy deskryptor pliku 1 zostanie zduplikowany na 3. Spowoduje to, że deskryptor pliku 1 nadal będzie odnosił się do terminala sterującego, a okno dialogowe TUI pojawi się na ekranie.
Teraz faktycznie jest krótka ręka dla bieżącego kontrolującego terminalu procesu, czyli /dev/tty
. W ten sposób rozwiązanie można uprościć bez użycia deskryptorów plików, po prostu w:
result=$(dialog --inputbox test 0 0 2>&1 1>/dev/tty);
echo "$result"
Najważniejsze rzeczy do zapamiętania:
- deskryptory plików są dziedziczone z powłoki przez każde polecenie
- zastępowanie poleceń jest realizowane jako potok
- zduplikowane deskryptory plików będą odnosić się do tego samego miejsca, co oryginalne, ale możemy manipulować każdym deskryptorem pliku osobno
Zobacz też
mktemp
polecenia, aby utworzyć plik tymczasowy.