Uczę się pisania skryptów bash i napotkałem problem. Napisałem skrypt, który pobiera dane wejściowe od użytkownika za pomocą polecenia „czytaj”, i zmieniam je na zmienną, która będzie używana później w skrypcie. Skrypt działa, ale ....
Chciałbym móc go skonfigurować za pomocą „okna dialogowego”. dowiedziałam się że
„dialog --inputbox” przekieruje dane wyjściowe do „stderr”, a aby uzyskać je jako zmienną, musisz skierować je do pliku, a następnie pobrać. Kod, który wyjaśniłem, to:
#!/bin/bash
dialog --inputbox \
"What is your username?" 0 0 2> /tmp/inputbox.tmp.$$
retval=$?
input=`cat /tmp/inputbox.tmp.$$`
rm -f /tmp/inputbox.tmp.$$
case $retval in
0)
echo "Your username is '$input'";;
1)
echo "Cancel pressed.";;
esac
Widzę, że wysyła sdterr do /tmp/inputbox.tmp.$$ za pomocą 2>, ale plik wyjściowy wygląda jak „inputbox.tmp.21661”. Gdy próbuję wyodrębnić plik, pojawia się błąd. Więc nadal nie mogę uzyskać danych wejściowych użytkownika z --inputbox jako zmiennej.
Przykładowy skrypt:
echo " What app would you like to remove? "
read dead_app
sudo apt-get remove --purge $dead_app
Jak widać, jest to podstawowy skrypt. Czy można w ogóle uzyskać zmienną jako słowo dialog --inputbox
?
mktemp
polecenia, aby utworzyć plik tymczasowy.Odpowiedzi:
^ odpowiedź od @Sneetsher (4 lipca 2014)
Zgodnie z prośbą postaram się wyjaśnić, co robi ten fragment kodu wiersz po wierszu.
Zauważ, że uprości to, pomijając wszystkie
;
średniki na końcach linii, ponieważ nie są one konieczne, jeśli napiszemy jedno polecenie w linii.I / O - Strumienie:
Najpierw musisz zrozumieć strumienie komunikacji. Istnieje 10 strumieni, ponumerowanych od 0 do 9:
Strumień 0 („STDIN”):
„Standardowe wejście”, domyślny strumień wejściowy do odczytu danych z klawiatury.
Strumień 1 („STDOUT”):
„Standardowe wyjście”, domyślny strumień wyjściowy używany do wyświetlania normalnego tekstu w terminalu.
Strumień 2 („STDERR”): „Błąd standardowy”, domyślny strumień wyjściowy używany do wyświetlania błędów lub innego tekstu do specjalnych celów w terminalu.
Strumienie 3-9:
Dodatkowe, swobodnie użyteczne strumienie. Nie są domyślnie używane i nie istnieją, dopóki coś nie spróbuje ich użyć.
Zauważ, że wszystkie „strumienie” są wewnętrznie reprezentowane przez deskryptory plików w
/dev/fd
(który jest dowiązaniem symbolicznym, do/proc/self/fd
którego zawiera inne dowiązanie symboliczne dla każdego strumienia ... jest to trochę skomplikowane i nie ma znaczenia dla ich zachowania, więc zatrzymuję się tutaj.). Standardowe strumienie mają również/dev/stdin
,/dev/stdout
i/dev/stderr
(które są linki symboliczne ponownie, etc ...).Scenariusz:
Wbudowanego Bash
exec
można zastosować do przekierowania strumienia do powłoki, co oznacza, że wpływa on na wszystkie następujące polecenia. Aby uzyskać więcej informacji, uruchomhelp exec
w swoim terminalu.W tym szczególnym przypadku strumień 3 zostaje przekierowany do strumienia 1 (STDOUT), co oznacza, że wszystko, co później wyślemy do strumienia 3, pojawi się w naszym terminalu, tak jakby normalnie było drukowane do STDOUT.
Ta linia składa się z wielu części i struktur składniowych:
result=$(...)
Ta struktura wykonuje polecenie w nawiasach i przypisuje dane wyjściowe (STDOUT) do zmiennej bash
result
. Jest czytelny$result
. Wszystko to jest jakoś opisane w bardzo długim czasieman bash
.dialog --inputbox TEXT HEIGHT WIDTH
To polecenie pokazuje pole TUI z danym TEKSTEM, pole wprowadzania tekstu oraz dwa przyciski OK i ANULUJ. Jeśli wybrano OK, polecenie kończy pracę ze statusem 0 i drukuje wprowadzony tekst do STDERR, jeśli zostanie wybrane ANULUJ, zakończy działanie z kodem 1 i nic nie wydrukuje. Aby uzyskać więcej informacji, przeczytaj
man dialog
.2>&1 1>&3
Są to dwa polecenia przekierowania. Będą interpretowane od prawej do lewej:
1>&3
przekierowuje strumień polecenia 1 (STDOUT) do strumienia niestandardowego 3.2>&1
następnie przekierowuje strumień polecenia 2 (STDERR) do strumienia 1 (STDOUT).Oznacza to, że wszystko, co polecenie wypisuje do STDOUT, pojawia się teraz w strumieniu 3, podczas gdy wszystko, co miało pojawić się na STDERR, jest teraz przekierowywane do STDOUT.
Tak więc cała linia wyświetla monit tekstowy (na STDOUT, który został przekierowany do strumienia 3, który powłoka ponownie przekierowuje z powrotem do STDOUT na końcu - patrz
exec 3>&1
polecenie) i przypisuje wprowadzone dane (zwrócone przez STDERR, a następnie przekierowane do STDOUT) do zmiennej Bashresult
.Ten kod pobiera poprzednio wykonany kod wyjścia polecenia (tutaj z
dialog
) przez zarezerwowaną zmienną Bash$?
(zawsze przechowuje ostatni kod wyjścia) i po prostu przechowuje go w naszej własnej zmiennej Bashexitcode
. Można go$exitcode
ponownie przeczytać . Możesz wyszukać więcej informacji na ten tematman bash
, ale może to chwilę potrwać ...Wbudowanego Bash
exec
można zastosować do przekierowania strumienia do powłoki, co oznacza, że wpływa on na wszystkie następujące polecenia. Aby uzyskać więcej informacji, uruchomhelp exec
w swoim terminalu.W tym szczególnym przypadku strumień 3 zostaje przekierowany do „stream -”, co oznacza, że powinien zostać zamknięty. Dane wysyłane do strumienia 3 nie będą już nigdzie przekierowywane.
To proste
echo
polecenie (więcej informacji na tematman echo
) po prostu drukuje zawartość dwóch zmiennych Bashresult
iexitcode
STDOUT. Ponieważ nie mamy już żadnych bezpośrednich lub niejawnych przekierowań strumienia, naprawdę pojawią się one w STDOUT, a zatem zostaną po prostu wyświetlone w terminalu. Co za cud! ;-)Streszczenie:
Najpierw ustawiamy powłokę tak, aby przekierowała wszystko, co wysyłamy do niestandardowego strumienia 3 z powrotem do STDOUT, tak aby pojawiła się w naszym terminalu.
Następnie uruchamiamy
dialog
polecenie, przekierowujemy jego oryginalny STDOUT do naszego niestandardowego strumienia 3, ponieważ musi on zostać wyświetlony na końcu, ale tymczasowo musimy użyć strumienia STDOUT do czegoś innego.Przekierowujemy oryginalny STDERR polecenia, w którym następnie zwracane są dane wejściowe użytkownika okna dialogowego, do STDOUT.
Teraz możemy przechwycić STDOUT (który przechowuje przekierowane dane z STDERR) i zapisać go w naszej zmiennej
$result
. Zawiera teraz żądane dane wejściowe użytkownika!Chcemy także
dialog
kodu wyjścia polecenia, który pokazuje nam, czy kliknięto OK czy ANULUJ. Ta wartość jest prezentowana w zarezerwowanej zmiennej Bash$?
i po prostu kopiujemy ją do własnej zmiennej$exitcode
.Następnie ponownie zamykamy strumień 3, ponieważ już go nie potrzebujemy, aby zatrzymać dalsze przekierowania.
Wreszcie, zwykle wyprowadzamy zawartość obu zmiennych
$result
(dane użytkownika w oknie dialogowym) i$exitcode
(0 dla OK, 1 dla CANCEL) do terminala.źródło
exec
jest niepotrzebnie skomplikowane. Dlaczego nie tylko my możemy--stdout
wybrać opcjędialog
lub przekierować jej wynik2>&1 >/dev/tty
?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 .
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 przypadkuresult=$(...)
część powinna zapisać wynik dowolnego polecenia wyznaczonego przez...
w zmiennej o nazwieresult
.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 swojegostdout
- deskryptora pliku 1, ten deskryptor pliku jest w rzeczywistości kopią deskryptora pliku 4, który jest końcem zapisu potoku. Zauważ, żestderr
tutaj nie gra żadnej roli, po prostu dlatego, że tostdout
tylko połączenie rur .Wróćmy na chwilę do pierwotnej odpowiedzi. Odkąd wiemy, że
dialog
zapisuje pole TUI dostdout
, odpowiedźstderr
i w ramach zastępowania komendstdout
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 jest2>&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 takgdzie
/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 toexec 3>&1
robi. Gdycommand > /dev/null
na przykład wywołujesz polecenie z przekierowaniem , powłoka przekazuje swój deskryptor standardowego pliku, a następnie używadup2()
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 zexec 3>&1
. Deskryptor pliku3
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, ale2>&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:Najważniejsze rzeczy do zapamiętania:
Zobacz też
źródło
--stdout
opcja może być niebezpieczna i łatwo zawiedzie w niektórych systemach, i myślę, że--output-fd 1
robi to samo:--stdout: Direct output to the standard output. This option is provided for compatibility with Xdialog, however using it in portable scripts is not recommended, since curses normally writes its screen updates to the standard output. If you use this option, dialog attempts to reopen the terminal so it can write to the display. Depending on the platform and your environment, that may fail.
- Jednak pomysł nazwanej rury jest fajny!--output-fd
czym ja tu nie skorzystałem--stdout
. Po drugie, okno dialogowe jest rysowane na pierwszym etapie, zwracane dane wyjściowe są na drugim miejscu. Nie robimy tych dwóch rzeczy jednocześnie. Jednak--output-fd
nie wymaga specjalnie użycia fd 1 (STDOUT). Można go łatwo przekierować do innego deskryptora pliku: DI nie mogę tego wyjaśnić !!! Jeśli rozumiesz, co mówią w odnośniku: Advanced Bash-Scripting Guide: Chapter 20. I / O Redirection , napisz nową odpowiedź, a dam ci 50repOtrzymano nagrodę , wyjaśnienia znajdują się w odpowiedzi ByteCommander . :) To część historii.
Źródło: Okno dialogowe w bash nie przechwytuje poprawnie zmiennych
Odwołanie: Zaawansowany przewodnik bash-scripting: Rozdział 20. Przekierowanie I / O
źródło
To działa dla mnie:
Strona podręcznika
dialog
mówi o --stdout:Czy ktoś może powiedzieć, na jakiej platformie lub środowisku to nie działa? Czy przekierowanie
dialog
wyjścia do2>&1 >/dev/tty
zamiast tego działa lepiej?źródło
W przypadku, gdy ktoś też wylądował tutaj z Google i chociaż to pytanie dotyczy konkretnie bash, oto inna alternatywa:
Możesz użyć zenity . Zenity to graficzne narzędzie, którego można używać w skryptach bash. Ale oczywiście wymagałoby to serwera X, jak słusznie zauważył użytkownik 877329.
Następnie w swoim skrypcie:
Przydatny link .
źródło
dialog
. To tak, jakbym przyszedł i zapytał: „Jak napisać to i to w pythonie?”, Ale dajesz mi wstrząs - bardzo się cieszę, że można to zrobić inaczej, ale nie o to pytamOdpowiedź udzielona przez Sneetshera jest nieco bardziej elegancka, ale mogę wyjaśnić, co jest nie tak: wartość
$$
różna (ponieważ uruchamia nową powłokę i$$
jest PID bieżącej powłoki). Będziesz chciał umieścić nazwę pliku w zmiennej, a następnie odwołać się do tej zmiennej w całym tekście.W takim przypadku lepszym rozwiązaniem byłoby uniknięcie pliku tymczasowego, ale w wielu sytuacjach nie można uniknąć pliku tymczasowego.
źródło