hannes22
Anmeldungsdatum: 20. August 2009
Beiträge: 266
|
Moin, ich hab mir 'nen Wolf geduckduckgot. Ich möchte in einem Terminal eine einfache Listenauswahl schreiben. Dafür muß ich wissen wieviele Zeilen dargestellt werden können. Ich schreibe das in C/C++ am liebsten mit Qt. Aber an der Zeilenfront scheint es mir etwas zu chaotisch zu sein. Was ich bisher festgestellt habe: Qt: frage nach der Zeilenzahl : nichts gefunden. Im Termial mit bash:
Liefert die Zeilenzahl. Wunderbar! Unter Qt: mit der Klasse QProcess "echo $Lines" ausgeführt, liefert einenleeren String. (Kann ich mir erklären, QProcess erzeugt einen neuen Prozess ohne Fenster - das hat natürlich keine Zeilen). In C:
| #include <stdio.h> /* printf */
#include <stdlib.h> /* getenv */
int main ()
{
char* pPath;
pPath = getenv ("LINES");
if (pPath!=NULL)
printf ("The current path is: %s",pPath);
return 0;
}
|
liefert NULL. Im Terminal:
liefert LINES=31
liefert nichts Und jetzt brauche ich hilfe: 1.) Wie bekomme ich unter C die Zeilen eines Terminals? 2.) Warum unterscheiden sich set und printenv? Danke schomal! Hannes
|
lubux
Anmeldungsdatum: 21. November 2012
Beiträge: 14314
|
hannes22 schrieb: Ich möchte in einem Terminal eine einfache Listenauswahl schreiben. Dafür muß ich wissen wieviele Zeilen dargestellt werden können.
Evtl. aus der config des Terminals. Z. B.:
:~$ cat /home/$USER/.config/lxterminal/lxterminal.conf | grep -i scrollback
scrollback=1500
|
Vain
Anmeldungsdatum: 12. April 2008
Beiträge: 2505
|
Moin, wenn du es direkt machen willst, dann benutzt du einen entsprechenden „ioctl “-Aufruf auf irgendeinem File-Deskriptor, der aufs Terminal zeigt:
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | #include <stdio.h>
#include <unistd.h>
#include <sys/ioctl.h>
int
main()
{
struct winsize w;
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
printf("w = %d, h = %d\n", w.ws_col, w.ws_row);
return 0;
}
|
Wenn du das Rad nicht neu erfinden möchtest, schau’ dir mal ncurses an. Zu „$LINES “: Das ist eine Variable, die von der Bash gesetzt wird. Die wird aber afaik nicht exportiert und steht Kindprozessen daher nicht zur Verfügung. Du solltest/kannst dich daher nicht auf diese Variable verlassen. Warum sich „set “ und „printenv “ unterscheiden? Das erste ist ein Shell-Builtin und das zweite ein separates Programm (ergo Kindprozess):
$ type set
set is a shell builtin
$ type printenv
printenv is hashed (/usr/bin/printenv)
|
D630
Anmeldungsdatum: 24. Juli 2013
Beiträge: 329
|
In der Shell gingen noch tput and stty , die nicht LINES auslesen, sondern wohl direkt abfragen?! | $ echo $SHELL
/bin/bash
$ type stty tput
stty is hashed (/bin/stty)
tput is hashed (/usr/bin/tput)
$ stty size
40 139
$ tput -S <<< lines$'\n'cols
40
139
|
:
|
hannes22
(Themenstarter)
Anmeldungsdatum: 20. August 2009
Beiträge: 266
|
Sehr interessant! Danke für die Erläuterungen, und die Lösung natürlich. Hannes
|
Dakuan
Anmeldungsdatum: 2. November 2004
Beiträge: 6488
Wohnort: Hamburg
|
Noch eine kleine Ergänzung zu ioctl. Wenn die Ausgabe in eine Datei umgelenkt wird, sind die Werte für ws_col und ws_row 0 was zu unangenehmen Überraschungen führen kann falls man darauf nicht vorbereitet ist.
|
Vain
Anmeldungsdatum: 12. April 2008
Beiträge: 2505
|
Dakuan schrieb: Noch eine kleine Ergänzung zu ioctl. Wenn die Ausgabe in eine Datei umgelenkt wird, sind die Werte für ws_col und ws_row 0 was zu unangenehmen Überraschungen führen kann falls man darauf nicht vorbereitet ist.
Ja, oder was ganz Verrücktes: $ ./bla >rofl
$ cat rofl
w = 22379, h = 42624 😀 In dem Zusammenhang kann man dann auch gleich noch die Funktion „isatty() “ erwähnen. Damit kannst du gucken, ob hinter STDOUT ein Terminal ist. 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | #include <stdio.h>
#include <unistd.h>
#include <sys/ioctl.h>
int
main()
{
struct winsize w;
if (isatty(STDOUT_FILENO))
{
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
printf("w = %d, h = %d\n", w.ws_col, w.ws_row);
}
else
printf("stdout is not a terminal.\n");
return 0;
}
|
|
Dakuan
Anmeldungsdatum: 2. November 2004
Beiträge: 6488
Wohnort: Hamburg
|
Ja, oder was ganz Verrücktes:
Das überrascht mich jetzt aber doch. Da habe ich bisher wohl immer Glück gehabt. Allerdings muss ich auch sagen, das ich die winsize Struktur immer global (also quasi statisch) angelegt habe, womit sie wohl immer vom Compiler initialisiert wird. Aber ich habe jetzt nochmal nachgesehen, wie ich das immer gemacht habe:
| ...
/* Terminalgeometrie abfragen, im Fehlerfall Dummywerte setzen */
if( ioctl( STDOUT_FILENO, TIOCGWINSZ, &term_win ) == -1 ){
term_win.ws_row = 24;
term_win.ws_col = 80; /* wg. Ausgabeumleitung in Datei */
}
line_length = term_win.ws_col;
...
|
man sollte also zusätzlich noch den Fehlercode abfragen. Das mit isatty() werde ich bei mir aber trotzdem nachrüsten.
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 13174
|
Zusätzlich würde ich STDIN und STDERR probieren, falls STDOUT nicht mit einem Terminal verbunden ist. Moment: wäre es nicht sogar noch besser, über das kontrollierende Terminal ("controlling terminal") zu gehen? Das müsste auch funktionieren, wenn STDIN, STDERR und STDOUT umgeleitet oder geschlossen sind.
|
Held-der-Arbeit
Anmeldungsdatum: 9. Januar 2006
Beiträge: 332
|
Hallo, da ich letztens ein ähnliches Problem hatte, will auch mal meinen Senf dazugeben. Ich habe das so gelöst: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 | #include <stdio.h>
#include <stdlib.h>
int get_terminal_lines();
int get_terminal_cols();
int main()
{
printf("Das Terminal hat %d Zeilen und %d Spalten.\n",get_terminal_lines(),get_terminal_cols());
return 0;
}
int get_terminal_lines()
{
FILE *fp;
char lines[10];
fp = popen("tput lines","r");
fgets(lines,sizeof lines,fp);
pclose(fp);
return atoi(lines);
}
int get_terminal_cols()
{
FILE *fp;
char cols[10];
fp = popen("tput cols","r");
fgets(cols,sizeof cols,fp);
pclose(fp);
return atoi(cols);
}
|
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 13174
|
Held-der-Arbeit schrieb:
Ich habe das so gelöst:
Da startest Du aber einen separaten Prozess. Abgesehen davon, dass das schief gehen kann, finde ich das ziemlich unelegant. Ich habe mir mal die Mühe gemacht und ein komplettes Beispiel in C zusammen gehackt: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 | #include <stdio.h>
#include <unistd.h>
#include <sys/ioctl.h>
int main(int argc, char* argv[]) {
char c_term [L_ctermid];
FILE *f;
/* find controlling terminal */
ctermid(c_term);
printf("cterm: %s\n", c_term);
/* now find terminal size if possible */
f = fopen(c_term, "w");
if ( f ) {
int fd = fileno(f);
if ( isatty(fd) ) {
struct winsize w;
ioctl(fd, TIOCGWINSZ, &w);
printf("rows: %4d columns: %4d\n", w.ws_row, w.ws_col);
}
else {
dprintf(2, "Not a tty!\n");
}
fclose(f);
}
else {
dprintf(2, "Cannot open %s\n", c_term);
}
return 0;
}
|
Die Ausgeben mit *printf sind nur zur Verdeutlichung. Die würde man dann in einem echten Programm weglassen. Für eine echte Anwendung müsste man das dann noch in eine Funktion auslagern, die vielleicht die Struct w oder NULL zurück liefert. Das sieht dann so aus: $ gcc -Wall -o ct-test ct.c && ./ct-test
cterm: /dev/pty0
rows: 60 columns: 132
$ stty -a | fgrep row
speed 38400 baud; rows 60; columns 132; line = 0; Der stty ist nur zur Kontrolle da. So sieht das dann aus, wenn man das Controlling Terminal abhängt: $ setsid ./ct-test
$ cterm: no tty
Not a tty!
|
Held-der-Arbeit
Anmeldungsdatum: 9. Januar 2006
Beiträge: 332
|
Was kann denn genau schief gehen, wenn ich mit popen() arbeite?
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 13174
|
Held-der-Arbeit schrieb: Was kann denn genau schief gehen, wenn ich mit popen() arbeite?
Der Pfad wird nicht gefunden, der Prozess kann nicht geforkt werden, Du kannst keine Dateideskriptoren für die Pipes mehr anlegen... Die Ausgabe eines Programms zu parsen, wenn man über Syscalls an die Informationen kommt, ist doch total umständlich - vor allem, da Du Dir mehr Abhängigkeiten einhandelst.
|