ubuntuusers.de

dialog --inputbox

Status: Gelöst | Ubuntu-Version: Kubuntu 11.10 (Oneiric Ocelot)
Antworten |

newbie

Anmeldungsdatum:
23. März 2006

Beiträge: 965

Hi! ☺

folgendes kleines Beispiel:

1
2
3
4
5
6
7
8
# Demonstriert dialog --inputbox
# Name : dialog4
name=`dialog --inputbox "Wie heißen Sie?" 0 0 "Jürgen" 3>&1 1>&2 2>&3`
# Dialog-Bildschirm löschen
dialog --clear
dialog --msgbox "Hallo $name, Willkommen bei $HOST!" 5 50
# Bildschirm löschen
clear

Was ich nicht verstehe, warum man hier einen zusätzlichen 3-en Kanal benötigt? Und wie wird folgendes (3>&1) richtig gelesen? 3>&1 bedeutet das, dass der Kanal 3 und 1 vereinigt werden? Heißt es, dass der Kanal 3 auf den Kanal 1 umgeleitet wird?

Danke!

Vain

Avatar von Vain

Anmeldungsdatum:
12. April 2008

Beiträge: 2510

Holdriho,

du kannst die Umleitungen ähnlich wie Variablenzuweisungen von links nach rechts lesen:

v3 = v1
v1 = v2
v2 = v3

Also auf gut deutsch: 1 und 2 werden vertauscht, dazu brauchst du 3 als temporären Speicher.

Bei „3>&1“ wird der Deskriptor 3 mit dem belegt, was momentan durch Deskriptor 1 angesprochen wird. „3 schreibt dorthin, wo 1 gerade hinschreibt.“ Das ist aber keine „Identität“ danach, beim folgenden „1>&2“ wird also der eben belegte Deskriptor 3 nicht verändert.

newbie

(Themenstarter)

Anmeldungsdatum:
23. März 2006

Beiträge: 965

Hi Vain ☺!

Danke für deine Antwort und deine Erklärung. dialog benutzt den Kanal 2, oder?

Folgendermaßen verstehe ich Deskriptoren: Kanal 0 ist für die Eingabe, 1 - Ausgabe auf dem Monitor, 2 - das selbe wie 1, jedoch nur für die Fehler vorgesehen. Habe ich das bis jetzt richtig verstanden? Warum müssen wir aber Kanal 1 mit Kanal 2 vertauschen? Ich verstehe das leider noch nicht.

Ich habe ehrlich gesagt den Sinn von Deskriptoren nicht wirklich verstanden. Warum braucht man für Fehlerausgabe einen zusätzlichen Kanal und benutzt nicht dafür bereits vorhanden Deskriptor 1? Und wofür braucht man weitere Kanäle 3 bis 9?

Danke!

Vain

Avatar von Vain

Anmeldungsdatum:
12. April 2008

Beiträge: 2510

Der Hintergrund der Aufteilung der Ausgabe in 1 und 2 ist, dass immer nur genau ein Kanal an eine Pipe geschickt werden kann. Doofes Beispiel:

ls /bin /ein/pfad | wc

STDOUT (1) von ls landet in der Pipe. Aber was, wenn es bei ls einen Fehler gibt? Den möchte ich unbedingt sehen! Ich will nicht, dass der auch in der Pipe landet und von wc verarbeitet wird. Deshalb gibt ls Fehler auf STDERR (2) aus. Deshalb kommt so eine Ausgabe hier zustande:

$ ls /bin /ein/pfad | wc
ls: cannot access /ein/pfad: No such file or directory
     91      91     566

dialog „missbraucht“ STDERR ein bisschen. Einerseits muss das Programm ja seine normale Ausgabe machen und dir den Dialog anzeigen. Dafür nimmt es STDOUT. Nun machst du im Dialog deine Eingabe – und dann? Irgendwohin muss diese Eingabe ausgegeben werden, denn du willst sie ja weiterverarbeiten. Dafür hast du keine andere Wahl als STDERR (doch, hast du in der Bash schon, aber es gibt auch wesentlich beschränkere Shells, die nicht so gut mit Deskriptoren umgehen können – ich denke da an die csh).

Nun schau dir nochmal das Konstrukt an:

name=$(dialog ...)

Das ist im Wesentlichen eine Pipe. dialog wird aufgerufen und seine Ausgabe über diese Pipe aufgefangen, sodass sie an $name zugewiesen werden kann. Wenn du dich an den Anfang des Postings erinnerst, dann ist die Frage jetzt beantwortet: Nur ein Kanal kann an die Pipe geleitet werden und das ist im Normalfall STDOUT. Dort landet aber der Dialog selbst und auf STDERR die Antwort des Benutzers – nur die willst du haben. Folglich musst du die beiden Kanäle vertauschen, damit das ehemalige STDOUT auf dem Bildschirm erscheint und das ehemalige STDERR in der Pipe.

Die weiteren Kanäle 3 bis 9 brauchst du manchmal, wenn du kompliziertere Dateioperationen machen willst. Du kannst bspw. eine Datei auf 3 zum Lesen öffnen und eine andere auf 4 zum Schreiben. Meistens kann man das auch anders lösen, aber eben nicht immer.

newbie

(Themenstarter)

Anmeldungsdatum:
23. März 2006

Beiträge: 965

Vielen Dank für deine ausführliche Antwort!

Ich schreibe dann auf, wie ich das ganze bis jetzt verstehe: Also ein Kanal/Deskriptor/Pipe (das bedeutet das selbe oder?) ist dazu da, um Ergebnisse bzw. Rückgabewerte weiterzugeben/weiterzuleiten. Und ein Programm wartet immer auf einen bestimmten Kanal. Im Beispiel mit ls und wc wartet das Programm wc immer auf den Kanal 1 (STDOUT). Bei einem Fehler schickt das Programm ls die Daten aber über den anderen Kanal (STDOUT bzw. 2), deshalb kommt beim wc nichts an und ich erhalte bei der Auswertung 0 (Null), weil der Kanal 1 leer ist.

1
2
3
ls /bin/lalala | wc
ls: Zugriff auf /bin/lalala nicht möglich: Datei oder Verzeichnis nicht gefunden
      0       0       0

Wenn ich aber die Fehlermeldung an wc weiterleiten will, dann muss ich den Rückgabewert, der im Kanal 2 gespeichert ist an den Kanal 1 weitergeben und das mache ich dann folgendermaßen:

1
2
ls /bin/lalala 2>&1 | wc
      1      11      82

Kanäle sind dann so zu sagen wie temporäre Variablen, die meine Rückgabewerte zwischenspeichern.

Habe ich das bis jetzt richtig verstanden?

In meiner Shell werden also alle Daten, die ich über die Tastatur eingebe als Rückgabewerte in den Kanal 0 gespeichert und dann von der Shell ausgewertet, richtig? Shell wertet dann die Daten aus und speichert das Ergebnis im Kanal 1 oder 2, dir mir dann auf dem Bildschirm angezeigt werden.

dialog „missbraucht“ STDERR ein bisschen. Einerseits muss das Programm ja seine normale Ausgabe machen und dir den Dialog anzeigen. Dafür nimmt es STDOUT. Nun machst du im Dialog deine Eingabe – und dann? Irgendwohin muss diese Eingabe ausgegeben werden, denn du willst sie ja weiterverarbeiten. Dafür hast du keine andere Wahl als STDERR (doch, hast du in der Bash schon, aber es gibt auch wesentlich beschränkere Shells, die nicht so gut mit Deskriptoren umgehen können – ich denke da an die csh).

Wenn ich etwas auswähle, ist das nicht wie eine Eingabe auf der Tastatur? Warum kann man dann den Kanal 0 dafür nicht verwenden? Ist er nicht für eine Eingabe vorgesehen oder ist er zu diesem Zeitpunkt mit anderem Rückgabewert belegt?

Nur ein Kanal kann an die Pipe geleitet werden und das ist im Normalfall STDOUT. Dort landet aber der Dialog selbst und auf STDERR die Antwort des Benutzers – nur die willst du haben. Folglich musst du die beiden Kanäle vertauschen, damit das ehemalige STDOUT auf dem Bildschirm erscheint und das ehemalige STDERR in der Pipe.

Wenn ich einen Wert einer Variable zuweise, wartet sie dann automatisch auf den Kanal 1 (STDOUT)? Also z.B.:

1
ergebnis=$(ls -l)

ls -l speichert dann den Rückgabewert in den Kanal 1 und die Variable ergebnis wartet auf einen Rückgabewert aus diesem Kanal? Befindet sich im folgenden Beispiel der Wert 12345 auch in dem Kanal 1?

1
ergebnis=1234

Sorry für so viele Fragen ☺. Danke dir!

Vain

Avatar von Vain

Anmeldungsdatum:
12. April 2008

Beiträge: 2510

Du bist relativ nahe dran, glaube ich, aber von manchen Dingen hast du eine falsche Vorstellung – so meine Vermutung. ☺

„Kanal/Deskriptor/Pipe“ ist nicht dasselbe. „Kanal“ und „Deskriptor“ kann man synonym verwenden, aber eine Pipe ist etwas anderes. Dazu später mehr. Ich bleibe jetzt mal beim Wort „Deskriptor“, weil ich das hier im Thread von Anfang an benutzt habe.

In Deskriptoren werden auch keine Rückgabewerte gespeichert. Ich glaube zwar, dass ich das die Tage schonmal aufgeschrieben habe, aber macht ja nix: Wenn du in einem Programm eine Datei öffnen willst, dann musst du sie zunächst natürlich über ihren Pfad adressieren. Du sagst also sinngemäß in C-ähnlichem Pseudocode:

open("/home/foo/datei.txt");

Aber wie willst du nun die geöffnete Datei ansprechen, um etwas hineinzuschreiben? Du könntest sie natürlich wieder mit ihrem Pfad ansprechen und etwas wie das hier machen:

write("/home/foo/datei.txt", "das hier soll geschrieben werden");

Das ist aber ziemlich umständlich, da jedesmal den ganzen Pfad hinzuschreiben. Unter anderem um diese Adressierung etwas einfacher zu machen, gibt es Deskriptoren. Das heißt, beim Öffnen der Datei passiert eigentlich etwas in dieser Richtung:

int fd = open("/home/foo/datei.txt");

In der Variablen fd steht dann eine Zahl, zum Beispiel die 3. Und beim Schreiben kannst du dann:

write(fd, "das hier soll geschrieben werden");
/* oder: */
write(3, "das hier soll geschrieben werden");

Allgemein formuliert gibt ein Deskriptor also ein Ziel für einen Schreibvorgang an – oder eine Quelle für einen Lesevorgang oder beides. Was genau sich hinter dem Deskriptor „3“ befindet, das spielt dann nämlich auch (fast) gar keine Rolle mehr. Deskriptoren sind eine sehr praktische Abstrahierung weg von der Idee „schreibe in Datei“ hin zu „schreibe auf Deskriptor“. Die Daten landen dann auch nicht „im“ Deskriptor, sondern in dem Ziel, auf das der Deskriptor zeigt.

Okay? ☺

So, was ist jetzt eine Pipe? Eine Pipe brauchst du überhaupt erst dann, wenn du zwei Prozesse miteinander sprechen lassen möchtest. Also im bisherigen Beispiel: ls und wc. Dabei soll wc auf das hören, was ls ihm zu sagen hat.

Wie löse ich das Problem nun möglichst einfach, ohne viele neue Programmierschnittstellen einzuführen? Ganz einfach: Mit Deskriptoren. Wie oben schon gesagt, ist so ein Deskriptor total abstrakt und das Programm, das auf ihn schreibt, interessiert sich nicht dafür, was „hinter“ dem Deskriptor ist.

Nun stell dir eine Pipe als ein Paar von Deskriptoren vor: Auf einem kannst du schreiben und auf dem anderen kannst du das lesen, was auf den ersten geschrieben wurde. Deine beiden Programme werden gestartet und ihnen werden die passenden Deskriptoren mitgegeben: ls bekommt das Ding zum Schreiben, wc das zum Lesen. Fertig ist die Pipe.

So viel erstmal allgemein zu Deskriptoren und Pipes. Macht es das etwas klarer? Mir rennt jetzt leider die Zeit davon, sodass ich auf deine konkreteren Fragen nicht mehr genauer eingehen kann. Vielleicht heute abend oder morgen bzw. am Wochenende. Ansonsten wird sicher jemand anders noch ein paar Worte dazu sagen. ☺

Vain

Avatar von Vain

Anmeldungsdatum:
12. April 2008

Beiträge: 2510

The saga continues:

newbie schrieb:

Und ein Programm wartet immer auf einen bestimmten Kanal. Im Beispiel mit ls und wc wartet das Programm wc immer auf den Kanal 1 (STDOUT).

Jein bzw. nein. Jedes Programm erbt erst einmal die offenen Deskriptoren seines Elternprozesses. Normalerweise ist dann STDIN (Deskriptor 0) zum Lesen, STDOUT (1) zum Schreiben der normalen Ausgabe und STDERR (2) für Fehler und Diagnoseinformationen gedacht. Aber nicht jedes Programm muss das auch wirklich nutzen. ls zum Beispiel will nichts von STDIN lesen, wc schon.

Übrigens: Des einen STDOUT ist in einer Pipe des anderen STDIN:

ls | wc

ls schreibt hier auf sein eigenes STDOUT (1) und wc liest von seinem STDIN (0). Jeder Prozesss hat seinen eigenen Satz an Deskriptoren

Bei einem Fehler schickt das Programm ls die Daten aber über den anderen Kanal (STDOUT bzw. 2), deshalb kommt beim wc nichts an und ich erhalte bei der Auswertung 0 (Null), weil der Kanal 1 leer ist.

1
2
3
ls /bin/lalala | wc
ls: Zugriff auf /bin/lalala nicht möglich: Datei oder Verzeichnis nicht gefunden
      0       0       0

Wenn ich aber die Fehlermeldung an wc weiterleiten will, dann muss ich den Rückgabewert, der im Kanal 2 gespeichert ist an den Kanal 1 weitergeben und das mache ich dann folgendermaßen:

1
2
ls /bin/lalala 2>&1 | wc
      1      11      82

Kanäle sind dann so zu sagen wie temporäre Variablen, die meine Rückgabewerte zwischenspeichern.

Habe ich das bis jetzt richtig verstanden?

Ja! ☺ Bis auf „Rückgabewert“, aber das hatte ich oben ja schon angesprochen.

In meiner Shell werden also alle Daten, die ich über die Tastatur eingebe als Rückgabewerte in den Kanal 0 gespeichert und dann von der Shell ausgewertet, richtig? Shell wertet dann die Daten aus und speichert das Ergebnis im Kanal 1 oder 2, dir mir dann auf dem Bildschirm angezeigt werden.

Hier müsste man „Shell“ und „Terminal“ noch trennen. Denn die Shell ist nur die Bash, die sich in der Regel nicht um STDIN und Tastatureingaben kümmert (es sei denn, du verwendest mal irgendwo read, dann wird von STDIN gelesen). Die Ausgabe der Shell, also zum Beispiel dein Prompt, landet aber auf dem STDOUT der Shell – was meistens das Terminal ist.

Das Terminal sitzt zwischen dir als Mensch und der Shell als Prozess. Es nimmt die Tastatureingaben entgegen und gibt sie an die Shell. Startest du über die Shell ein Programm, dann ist die Shell gar nicht mehr direkt aktiv und das Terminal schickt die Eingaben direkt an dieses Programm.

(Heute ist das so falsch, weil ein Terminal kein eigenes Gerät mehr ist. Eigentlich sitzt jetzt das Terminal zwischen dem Kernel und der Shell. Aber das ist auch ein bisschen Erbsenzählerei.)

dialog „missbraucht“ STDERR ein bisschen. Einerseits muss das Programm ja seine normale Ausgabe machen und dir den Dialog anzeigen. Dafür nimmt es STDOUT. Nun machst du im Dialog deine Eingabe – und dann? Irgendwohin muss diese Eingabe ausgegeben werden, denn du willst sie ja weiterverarbeiten. Dafür hast du keine andere Wahl als STDERR (doch, hast du in der Bash schon, aber es gibt auch wesentlich beschränkere Shells, die nicht so gut mit Deskriptoren umgehen können – ich denke da an die csh).

Wenn ich etwas auswähle, ist das nicht wie eine Eingabe auf der Tastatur? Warum kann man dann den Kanal 0 dafür nicht verwenden? Ist er nicht für eine Eingabe vorgesehen oder ist er zu diesem Zeitpunkt mit anderem Rückgabewert belegt?

Du siehst das schon richtig. Deine Tastatureingabe kommt über STDIN (0) an dialog. Dann verrechnet dialog das und sendet eventuell eine Ausgabe an STDOUT bzw. STDERR.

Ich hatte hierbei „Eingabe“ als etwas anderes gemeint und zwar als die komplette Eingabe, die du im Dialog tätigst. Zum Beispiel das Auswählen eines Listenelements – das ist deine Eingabe, die ich meinte. Der springende Punkt ist jetzt, dass dialog diese Auswahl/Eingabe dem aufrufenden Skript irgendwie mitteilen muss. Und erst hier kommt das STDOUT/STDERR-Problem zum Tragen.

Wenn ich einen Wert einer Variable zuweise, wartet sie dann automatisch auf den Kanal 1 (STDOUT)? Also z.B.:

1
ergebnis=$(ls -l)

ls -l speichert dann den Rückgabewert in den Kanal 1 und die Variable ergebnis wartet auf einen Rückgabewert aus diesem Kanal?

Wenn man von „Rückgabewert“ wieder absieht: Ja!

Befindet sich im folgenden Beispiel der Wert 12345 auch in dem Kanal 1?

1
ergebnis=1234

Nee, das ist eine ganz normale Variablenzuweisung. Die Deskriptoren/Kanäle spielen nur eine Rolle, wenn externe Programme gestartet werden und deren Ausgabe aufgefangen wird.

newbie

(Themenstarter)

Anmeldungsdatum:
23. März 2006

Beiträge: 965

Ich möchte mich für deine sehr ausführliche Antworten bedanken und vor allem für deine Zeit!

Das ist für mich noch ein schwieriges Thema und ich muss zunächst alle Informationen "verdauen" bzw. verarbeiten, um alles zu verstehen. Ich werde deine Antworten noch mehrmals sehr genau durchlesen müssen und mir einige Beispiele aus dem Buch nochmal anschauen. Dazu werde ich vermutlich aber erst morgen kommen. Ich hoffe, dass ich jetzt dank deinen Antworten diese Beispiele vollständig verstehen werde. Wenn nicht, dann kann ich ja entweder hier nochmal fragen oder ein neues Thema aufmachen ☺. Du hast mir sehr weitergeholfen und meine Fragen beantwortet, deshalb markiere ich das Thema als gelöst.

Nochmals vielen Dank! 👍

Antworten |