![]() |
![]() |
|
Das Ganze gelingt mit od auch im ASCII-Format, nur dass das Leerzeichen als ein »Leerzeichen« angezeigt wird: you@host > echo -n "$IFS" | od -c 0000000 \t \n 0000003 Diese voreingestellten Trennzeichen der Shell dienen der Trennung der Eingabe für den Befehl read, der Variablen- sowie auch der Kommando-Substitution. Wenn Sie also z. B. read folgendermaßen verwenden you@host > read nachname vorname alter Wolf Jürgen 30 you@host > echo $vorname Jürgen you@host > echo $nachname Wolf you@host > echo $alter 30 so verdanken Sie es der Variablen IFS, dass hierbei die entsprechenden Eingaben an die dafür vorgesehenen Variablen übergeben wurden. Häufig war aber in den Kapiteln zuvor die Rede davon, die Variable IFS den eigenen Bedürfnissen anzupassen – sprich: das oder die Trennzeichen zu verändern. Wollen Sie beispielsweise, dass bei der Eingabe von read ein Semikolon statt eines Leerzeichens als Trennzeichen dient, so lässt sich dies einfach erreichen: you@host > BACKIFS="$IFS" you@host > IFS=\; you@host > echo -n "$IFS" | od -c 0000000 ; 0000001 you@host > read nachname vorname alter Wolf;Jürgen;30 you@host > echo $vorname Jürgen you@host > echo $nachname Wolf you@host > echo $alter 30 you@host > IFS=$BACKIFS Zuerst wurde eine Sicherung der Variablen IFS in BACKIFS vorgenommen. Der Vorgang, ein Backup von IFS zu erstellen, und das anschließende Wiederherstellen ist wichtiger, als dies auf den ersten Blick erscheint. Unterlässt man es, kann man sich auf das aktuelle Terminal nicht mehr verlassen, da einige Programme nur noch Unsinn fabrizieren. Als Nächstes übergeben Sie im Beispiel der Variablen IFS ein Semikolon (geschützt mit einem Backslash). Daraufhin folgt dasselbe Beispiel wie zuvor, nur mit einem Semikolon als Trennzeichen. Am Ende stellen wir den ursprünglichen Wert von IFS wieder her. Natürlich spricht auch nichts dagegen, IFS mit mehr als einem Trennzeichen zu versehen, beispielsweise: you@host > IFS=$BACKIFS you@host > IFS=:, you@host > read nachname vorname alter Wolf,Jürgen:30 you@host > echo $vorname Jürgen you@host > echo $nachname Wolf you@host > echo $alter 30 you@host > IFS=$BACKIFS Im Beispiel wurde IFS mit den Trennzeichen : und , definiert. Wollen Sie bei einer Eingabe mit read, dass IFS nicht immer die führenden Leerzeichen (falls vorhanden) entfernt, so müssen Sie IFS nur mittels IFS= auf »leer« setzen: you@host > IFS=$BACKIFS
you@host > read var
Hier sind führende Leerzeichen vorhanden
you@host > echo $var
Hier sind führende Leerzeichen vorhanden
you@host > IFS=
you@host > read var
Hier sind führende Leerzeichen vorhanden
you@host > echo $var
Hier sind führende Leerzeichen vorhanden
you@host > IFS=$BACKIFS
Gleiches wird natürlich auch gern bei einer Variablen-Substitution verwendet. Im folgenden Beispiel wird die Variable durch ein Minuszeichen als Trenner zerteilt und mittels set auf die einzelnen Positionsparameter zerstückelt. # Demonstriert die Verwendung von IFS # Name : aifs1 # IFS sichern BACKIFS="$IFS" # Minuszeichen als Trenner IFS=- counter=1 var="Wolf-Jürgen-30-Bayern" # var anhand von Trennzeichen in IFS auftrennen set $var # Ein Zugriff auf $1, $2, ... wäre hier auch möglich for daten in "$@" do echo "$counter. $daten" counter=`expr $counter + 1` done IFS=$BACKIFS Das Script bei der Ausführung: you@host > ./aifs1 1. Wolf 2. Jürgen 3. 30 4. Bayern Wenn Sie das Script ein wenig umschreiben, können Sie hiermit zeilenweise alle Einträge der Shell-Variablen PATH ausgeben lassen, welche ja durch einen Doppelpunkt getrennt werden. # Demonstriert die Verwendung von IFS # Name : aifs2 # IFS sichern BACKIFS="$IFS" # Minuszeichen als Trenner IFS=: # PATH anhand von Trennzeichen in IFS auftrennen set $PATH for path in "$@" do echo "$path" done IFS=$BACKIFS Das Script bei der Ausführung: you@host > ./aifs2 /home/tot/bin /usr/local/bin /usr/bin /usr/X11R6/bin /bin /usr/games /opt/gnome/bin /opt/kde3/bin /usr/lib/java/jre/bin Natürlich hätte man dies wesentlich effektiver mit folgender Zeile lösen können: you@host > echo $PATH | tr ':' '\n' Zu guter Letzt sei erwähnt, dass die Variable IFS noch recht häufig in Verbindung mit einer Kommando-Substitution verwendet wird. Wollen Sie etwa mittels grep nach einem bestimmten User in /etc/passwd suchen, wird meistens eine Ausgabe wie folgt genutzt: you@host > grep you /etc/passwd you:x:1001:100::/home/you:/bin/bash Dies in die Einzelteile zu zerlegen, sollte Ihnen jetzt mit der Variablen IFS nicht mehr schwer fallen. # Demonstriert die Verwendung von IFS # Name : aifs3 # IFS sichern BACKIFS="$IFS" # Minuszeichen als Trenner IFS=: if [ $# -lt 1 ] then echo "usage: $0 User" exit 1 fi # Ausgabe anhand von Trennzeichen in IFS auftrennen set `grep ^$1 /etc/passwd` echo "User : $1" echo "User-Nummer : $3" echo "Gruppen-Nummer : $4" echo "Home-Verzeichnis : $6" echo "Start-Shell : $7" IFS=$BACKIFS Das Script bei der Ausführung: you@host > ./aifs3 you User : you User-Nummer : 1001 Gruppen-Nummer : 100 Home-Verzeichnis : /home/you Start-Shell : /bin/bash you@host > ./aifs3 tot User : tot User-Nummer : 1000 Gruppen-Nummer : 100 Home-Verzeichnis : /home/tot Start-Shell : /bin/ksh you@host > ./aifs3 root User : root User-Nummer : 0 Gruppen-Nummer : 0 Home-Verzeichnis : /root Start-Shell : /bin/ksh 5.3.7 Arrays einlesen mit read (Bash und Korn-Shell only)
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Hinweis In der Bash können Sie auch die Abfrage einzelner Zeichen mit read und der Option -n vornehmen. Schreiben Sie etwa read -n 1 var, befindet sich in var das einzelne Zeichen. Allerdings unterstützt dies nur darstellbare ASCII-Zeichen. Bei Zeichen mit Escape-Folgen (siehe nächster Abschnitt) taugt auch dies nicht mehr. |
Im Beispiel zuvor haben Sie gesehen, wie Sie einzelne Tastendrücke mithilfe der Kommandos stty und dd abfragen können. Aber sobald Sie hierbei Tastendrücke wie (F1), (F2), (ESC), (˝), (Ľ) usw. abfragen wollen, wird dies nicht mehr funktionieren. Testen Sie am besten selbst unser abgespecktes Script von eben:
# Demonstriert das Einlesen einzelner Zeichen # Name : areadchar2 echo "Bitte eine Taste betätigen" stty raw -echo char=`dd bs=1 count=1 2>/dev/null` stty -raw echo echo "Die Taste war $char"
Das Script bei der Ausführung:
you@host > ./areadchar2 Bitte eine Taste betätigen (A) Die Taste war A you@host > ./areadchar2 Bitte eine Taste betätigen (ESC) Die Taste war you@host > ./areadchar2 Bitte eine Taste betätigen (F1) Die Taste war [A
Das Problem mit den Pfeil- oder Funktionstasten ist nun mal, dass hierbei Escape-Folgen zurückgegeben werden. Schlimmer noch, die Escape-Folgen sind häufig unterschiedlich lang und – um es noch schlimmer zu machen – abhängig vom Typ des Terminals und zusätzlich auch noch der nationalen Besonderheit (Umlaute und Ähnliches).
Somit scheint eine portable Lösung des Problems fast unmöglich, es sei denn, man kennt sich ein bisschen mit C aus. Da ich dies nicht voraussetzen kann, gebe ich Ihnen hier ein einfaches Rezept an die Hand, welches Sie bei Bedarf erweitern können.
Zuerst müssen Sie sich darum kümmern, wie die Escape-Sequenzen für entsprechende Terminals aussehen. Welches Terminal Sie im Augenblick nutzen, können Sie mit echo $TERM in Erfahrung bringen. Im nächsten Schritt können Sie den Befehl infocmp verwenden, um vom entsprechenden Terminaltyp Informationen zu den Tasten zu erhalten.
you@host > echo $TERM
xterm
you@host > infocmp
# Reconstructed via infocmp from file: /usr/share/terminfo/x/xterm
xterm|xterm terminal emulator (X Window System),
am, bce, km, mc5i, mir, msgr, npc, xenl,
colors#8, cols#80, it#8, lines#24, pairs#64,
bel=^G, blink=\E[5m, bold=\E[1m, cbt=\E[Z, civis=\E[?25l,
clear=\E[H\E[2J, cnorm=\E[?12l\E[?25h, cr=^M,
csr=\E[%i%p1 %d;%p2 %dr, cub=\E[%p1 %dD, cub1=^H,
cud=\E[%p1 %dB, cud1=^J, cuf=\E[%p1 %dC, cuf1=\E[C,
cup=\E[%i%p1 %d;%p2 %dH, cuu=\E[%p1 %dA, cuu1=\E[A,
cvvis=\E[?12;25h, dch=\E[%p1 %dP, dch1=\E[P, dl=\E[%p1 %dM,
dl1=\E[M, ech=\E[%p1 %dX, ed=\E[J, el=\E[K, el1=\E[1K,
enacs=\E(B\E)0, flash=\E[?5h$<100/>\E[?5l, home=\E[H,
hpa=\E[%i%p1 %dG, ht=^I, hts=\EH, ich=\E[%p1 %d@,
il=\E[%p1 %dL, il1=\E[L, ind=^J, indn=\E[%p1 %dS,
invis=\E[8m, is2=\E[!p\E[?3;4l\E[4l\E>, kDC=\E[3;2~,
kEND=\E[1;2F, kHOM=\E[1;2H, kIC=\E[2;2~, kLFT=\E[1;2D,
kNXT=\E[6;2~, kPRV=\E[5;2~, kRIT=\E[1;2C, kb2=\EOE,
kbs=\177, kcbt=\E[Z, kcub1=\EOD, kcud1=\EOB, kcuf1=\EOC,
kcuu1=\EOA, kdch1=\E[3~, kend=\EOF, kent=\EOM, kf1=\EOP,
kf10=\E[21~, kf11=\E[23~, kf12=\E[24~, kf13=\EO2P,
...
Hierbei werden Ihnen zunächst eine Menge Informationen um die Ohren gehauen, welche auf den ersten Blick wie Zeichensalat wirken. Bei genauerem Hinsehen aber können Sie alte Bekannte aus Abschnitt 5.2.4 zur Steuerung des Terminals mit tput wieder entdecken.
|
Hinweis Wenn Sie über kein infocmp auf Ihrem Rechner verfügen, liegt das wahrscheinlich daran, dass das ncurses-devel-Paket nicht installiert ist. Diesem Paket liegt auch infocmp bei. |
infocmp gibt Ihnen hier Einträge wie
cuu1=\E[A
zurück. Dass man daraus nicht sonderlich schlau wird, leuchtet ein. Daher sollten Sie die Manual-Page von terminfo(5) befragen (oder genauer absuchen), wofür denn hier cuu1 steht:
you@host > man 5 terminfo | grep cuu1
Formatiere terminfo(5) neu, bitte warten...
cursor_up cuu1 up up one line
key_up kcuu1 ku up-arrow key
micro_up mcuu1 Zd Like cursor_up in
kcuf1=\E[C, kcuu1=\E[A, kf1=\E[M, kf10=\E[V,
Also, die Pfeil-nach-oben-Taste (cursor_up) haben wir hier. Und diese wird bei der xterm mit \E[A »dargestellt«. Das \E ist hierbei das Escape-Zeichen und besitzt den ASCII-Codewert 27. Somit sieht der C-Quellcode zum Überprüfen der Pfeil-nach-oben-Taste wie folgt aus (fett hervorgehoben ist hier die eigentliche Überprüfung, die Sie bei Bedarf erweitern können):
/* getkey.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <termios.h>
enum { ERROR=-1, SUCCESS, ONEBYTE };
/*Altes Terminal wiederherstellen*/
static struct termios BACKUP_TTY; /*Altes Terminal wiederherstellen*/
/* Eingabekanal wird so umgeändert, damit die Tasten einzeln */
/* abgefragt werden können */
int new_tty(int fd) {
struct termios buffer;
/* Wir fragen nach den Attributen des Terminals und übergeben */
/* diese dann an buffer. BACKUP_TTY dient bei Programmende zur */
/* Wiederherstellung der alten Attribute und bleibt unberührt. */
if((tcgetattr(fd, &BACKUP_TTY)) == ERROR)
return ERROR;
buffer = BACKUP_TTY;
/* Lokale Flags werden gelöscht : */
/* ECHO = Zeichenausgabe auf Bildschirm */
/* ICANON = Zeilenorientierter Eingabemodus */
/* ISIG = Terminal Steuerzeichen */
buffer.c_lflag = buffer.c_lflag & ~(ECHO|ICANON|ISIG);
/* VMIN=Anzahl der Bytes, die gelesen werden müssen, bevor */
/* read() zurückkehrt In unserem Beispiel 1Byte für 1 Zeichen */
buffer.c_cc[VMIN] = 1;
/* Wir setzen jetzt die von uns gewünschten Attribute */
if((tcsetattr(fd, TCSAFLUSH, &buffer)) == ERROR)
return ERROR;
return SUCCESS;
}
/* Ursprüngliches Terminal wiederherstellen */
int restore_tty(int fd) {
if((tcsetattr(fd, TCSAFLUSH, &BACKUP_TTY)) == ERROR)
return ERROR;
return SUCCESS;
}
int main(int argc, char **argv) {
int rd;
char c, buffer[10];
/* Setzen des neuen Modus */
if(new_tty(STDIN_FILENO) == ERROR) {
printf("Fehler bei der Funktion new_tty()\n");
exit(EXIT_FAILURE);
}
/* Erste Zeichen lesen */
if(read(STDIN_FILENO, &c, 1) < ONEBYTE) {
printf("Fehler bei read()\n");
restore_tty(STDIN_FILENO);
exit(EXIT_FAILURE);
}
/* Haben wir ein ESC ('\E') gelesen? */
if(c == 27) {
/* Jep eine Escape-Sequenz, wir wollen den Rest */
/* der Zeichen auslesen */
rd=read(STDIN_FILENO, buffer, 4);
/*String terminieren*/
buffer[rd]='\0';
/* Hier erfolgt die Überprüfung des Tastendrucks*/
/* Wars der Pfeil-nach-oben \E[A */
if(strcmp(buffer,"[A") == SUCCESS)
printf("Pfeil-nach-oben betätigt\n");
/* Nein, keine Escape-Sequenz */
}
else {
if((c < 32) || (c == 127))
printf("--> %d\n",c); /* Numerischen Wert ausgeben */
else
printf("--> %c\n",c); /* Zeichen ausgeben */
}
restore_tty(STDIN_FILENO);
return EXIT_SUCCESS;
}
Wird das Programm kompiliert, übersetzt und anschließend ausgeführt, erhalten Sie folgende Ausgabe:
you@host > gcc -Wall -o getkey getkey.c you@host > ./getkey (1) --> 1 you@host > ./getkey (A) --> A you@host > ./getkey (˝) Pfeil-nach-oben betätigt
Wie Sie jetzt im Beispiel vorgegangen sind, können Sie auch mit anderen Pfeil- und Funktionstasten vorgehen. Den Pfeil-nach-rechts können Sie z. B. wie folgt implementieren:
if(strcmp(buffer,"[D") == SUCCESS)
printf("Pfeil-nach-rechts\n");
Aber anstatt einer Ausgabe empfehle ich Ihnen, einen Rückgabewert mittels return zu verwenden, etwa:
...
/* Hier erfolgt die Überprüfung des Tastendrucks*/
/* Wars der Pfeil-nach-oben \E[A */
if(strcmp(buffer,"[A") == SUCCESS) {
restore_tty(STDIN_FILENO);
return 10; /* Rückgabewert für unser Shellscript */
}
/* Wars der Pfeil-nach-links */
if(strcmp(buffer,"[D") == SUCCESS) {
restore_tty(STDIN_FILENO);
return 11; /* Rückgabewert für unser Shellscript */
}
...
Damit können Sie das Beispiel auch mit einem Shellscript verwenden, indem Sie die Variable $? abfragen. Hier das Script:
# Demonstriert das Einlesen einzelner Zeichen # Name : areadchar3 echo "Bitte eine Taste betätigen" ./getkey ret=$? if [ $ret -eq 0 ] then echo "Keine Pfeiltaste" fi if [ $ret -eq 10 ] then echo "Die Pfeil-nach-oben-Taste wars" fi if [ $ret -eq 11 ] then echo "Die Pfeil-nach-links-Taste wars" fi
Übersetzen Sie das C-Programm von Neuem mit den geänderten Zeilen Code und führen Sie das Shellscript aus:
you@host > ./areadchar3 Bitte eine Taste betätigen (d)--> d Keine Pfeiltaste you@host > ./areadchar3 Bitte eine Taste betätigen (˝)Die Pfeil-nach-oben-Taste wars you@host > ./areadchar3 Bitte eine Taste betätigen (ć)Die Pfeil-nach-links-Taste wars
Mit dem C-Programm haben Sie quasi Ihr eigenes Tool geschrieben, welches Sie am besten in ein PATH-Verzeichnis kopieren und auf welches Sie somit jederzeit zurückgreifen und natürlich erweitern können/sollen.
|
Hinweis Natürlich wurde das Thema hier nur gestreift, aber es würde keinen Sinn ergeben, noch mehr einzusteigen, da ich von Ihnen nicht auch noch C-Kenntnisse verlangen kann. Aber Sie konnten hierbei schon sehen, dass Sie mit zusätzlichen Programmierkenntnissen noch viel weiter kommen können. Fehlt ein bestimmtes Tool, programmiert man eben selbst eins. Und noch ein Hinweis Noch mehr Lust auf die C-Programmierung bekommen? Ich könnte Ihnen mein Buch »C von A bis Z« empfehlen oder etwas zur Linux-UNIX-Programimerung. Auch hierzu finden Sie ein Buch aus meiner Feder (»Linux-Unix-Programmierung«). Beide Bücher sind bei Galileo Press erschienen. |
Wollen Sie in Ihrem Script eine Passworteingabe vornehmen und darauf verzichten, dass die Eingabe auf dem Bildschirm mit ausgegeben wird, können Sie auch hierfür stty verwenden. Hierbei reicht es aus, die Ausgabe (echo) abzuschalten und nach der Passworteingabe wieder zu aktivieren.
stty -echo # Passwort einlesen stty echo
Ein Script als Beispiel:
# Demonstriert das Einlesen einzelner Zeichen # Name : anoecho printf "Bitte Eingabe machen: " # Ausgabe auf dem Bildschirm abschalten stty -echo read passwort # Ausgabe auf dem Bildschirm wieder einschalten stty echo printf "\nIhre Eingabe lautete : %s\n" $passwort
Das Script bei der Ausführung:
you@host > ./anoecho Bitte Eingabe machen: Ihre Eingabe lautete: k3l5o6i8
Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.
| << zurück |
|
||||||||||||
|
||||||||||||
|
||||||||||||
Copyright © Rheinwerk Verlag GmbH 2005
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt.
Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.