C에서 터미널 너비를 얻습니까?


91

내 C 프로그램 내에서 터미널 너비를 얻는 방법을 찾고 있습니다. 내가 계속 생각하는 것은 다음과 같은 것입니다.

#include <sys/ioctl.h>
#include <stdio.h>

int main (void)
{
    struct ttysize ts;
    ioctl(0, TIOCGSIZE, &ts);

    printf ("lines %d\n", ts.ts_lines);
    printf ("columns %d\n", ts.ts_cols);
}

하지만 내가 시도 할 때마다

austin@:~$ gcc test.c -o test
test.c: In function ‘main’:
test.c:6: error: storage size of ‘ts’ isn’t known
test.c:7: error: ‘TIOCGSIZE’ undeclared (first use in this function)
test.c:7: error: (Each undeclared identifier is reported only once
test.c:7: error: for each function it appears in.)

이것이 가장 좋은 방법입니까, 아니면 더 나은 방법이 있습니까? 그렇지 않다면 어떻게 작동시킬 수 있습니까?

편집 : 고정 코드는

#include <sys/ioctl.h>
#include <stdio.h>

int main (void)
{
    struct winsize w;
    ioctl(0, TIOCGWINSZ, &w);

    printf ("lines %d\n", w.ws_row);
    printf ("columns %d\n", w.ws_col);
    return 0;
}

1
제안 된 답변 중 절반 이상이 정확하지 않습니다.
Thomas Dickey

2
@ThomasDickey, 당신의 대답은 어디입니까?
Alexis Wilke 19

답변:


127

getenv () 사용을 고려해 보셨습니까? ? 터미널 열과 행을 포함하는 시스템의 환경 변수를 가져올 수 있습니다.

또는 방법을 사용하여 커널이 터미널 크기로 보는 것을보고 싶다면 (터미널 크기가 조정 된 경우 더 좋음) 다음과 같이 TIOCGSIZE가 아닌 TIOCGWINSZ를 사용해야합니다.

struct winsize w;
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);

그리고 전체 코드 :

#include <sys/ioctl.h>
#include <stdio.h>
#include <unistd.h>

int main (int argc, char **argv)
{
    struct winsize w;
    ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);

    printf ("lines %d\n", w.ws_row);
    printf ("columns %d\n", w.ws_col);
    return 0;  // make sure your main returns int
}

7
예, 그러나 용어 너비는 환경 변수가 아니며 용어에 정적입니다.
오스틴

4
프로그램 실행 중에 누군가가 터미널 크기 를 조정하면 현재 터미널 크기를 제공하지 않습니다 .
Chris Jester-Young

그래, 그 :) 추가되었다
존 T

픽셀 크기를 얻는 방법? 나는 ws_xpixeland를 사용 ws_ypixel했지만 그냥 0을 인쇄합니다!
Debashish

@Debashish는 의존합니다. 예를 들어 Linux는 이러한 필드를 전혀 지원하지 않습니다.
melpomene

16

이 예제는 약간 긴 편이지만 터미널 치수를 감지하는 가장 이식 가능한 방법이라고 생각합니다. 이것은 또한 크기 조정 이벤트를 처리합니다.

tim과 rlbond가 제안했듯이 ncurses를 사용하고 있습니다. 환경 변수를 직접 읽는 것과 비교하여 터미널 호환성이 크게 향상됩니다.

#include <ncurses.h>
#include <string.h>
#include <signal.h>

// SIGWINCH is called when the window is resized.
void handle_winch(int sig){
  signal(SIGWINCH, SIG_IGN);

  // Reinitialize the window to update data structures.
  endwin();
  initscr();
  refresh();
  clear();

  char tmp[128];
  sprintf(tmp, "%dx%d", COLS, LINES);

  // Approximate the center
  int x = COLS / 2 - strlen(tmp) / 2;
  int y = LINES / 2 - 1;

  mvaddstr(y, x, tmp);
  refresh();

  signal(SIGWINCH, handle_winch);
}

int main(int argc, char *argv[]){
  initscr();
  // COLS/LINES are now set

  signal(SIGWINCH, handle_winch);

  while(getch() != 27){
    /* Nada */
  }

  endwin();

  return(0);
}

3
그러나 신호 처리기에서 initscr 및 endwin을 호출하는 것이 정말 안전합니까? 그들은 적어도 async-signal-safe API에 포함되지 않았습니다man 7 signal
nav

1
@nav의 좋은 점입니다. 저는 그런 생각을 한 적이 없습니다 ! 더 나은 솔루션은 신호 처리기가 플래그를 발생시킨 다음 메인 루프에서 나머지 작업을 수행하도록하는 것입니까?
gamen

1
@gamen, 예, 그게 더 좋을 것입니다;)-또한 신호 대신 sigaction을 사용하는 것이 더 좋습니다.
Bodo Thiesen 2015

그렇다면 COLS와 LINES 전역 변수는 무엇입니까?
einpoklum

1
@AlexisWilke : OKERR. 어떻게 우리가 그 :-( 우리의 삶에 틈 메우는 데 도움에의 "종류"
einpoklum

12
#include <stdio.h>
#include <stdlib.h>
#include <termcap.h>
#include <error.h>

static char termbuf[2048];

int main(void)
{
    char *termtype = getenv("TERM");

    if (tgetent(termbuf, termtype) < 0) {
        error(EXIT_FAILURE, 0, "Could not access the termcap data base.\n");
    }

    int lines = tgetnum("li");
    int columns = tgetnum("co");
    printf("lines = %d; columns = %d.\n", lines, columns);
    return 0;
}

로 컴파일해야 -ltermcap합니다. termcap을 사용하여 얻을 수있는 다른 유용한 정보가 많이 있습니다. info termcap자세한 내용 은 termcap 설명서를 확인 하십시오.


-lcurses로도 컴파일 할 수 있습니다.
Kambus 2011

2
나는이 댓글 6 년 사후 온다 알고 있지만, 2048의 마법 번호 ... 설명해주십시오
einpoklum

1
@einpoklum 이것은 거의 3 년이 지난 지금이지만, 2048은 어떤 입력 문자열이 거기에 들어가는 지에 대해 "아마 충분히 커야 만하는"버퍼의 임의의 크기라는 것이 분명하지 않습니까?
Roflcopter4

2
사실,이 답변은 정확하기에는 너무 많은 가정을합니다.
Thomas Dickey

1
궁금한 사람을 위해 2048 버퍼 크기는 GNU termcap 문서 ( gnu.org/software/termutils/manual/termcap-1.3/html_mono/… )에 설명되어 있습니다. 이 게시물을 읽는 사람들이 유용 할 수있는 다른 내용도 많이 있습니다. .

3

ncurses가 설치되어 있고 사용중인 경우을 사용 getmaxyx()하여 터미널의 크기를 찾을 수 있습니다 .


2
는 Y 먼저 다음 X. 온다 네, 그리고 메모를 할
다니엘

0

Linux를 사용한다고 가정하면 대신 ncurses 라이브러리 를 사용하고 싶습니다 . 나는 당신이 가지고있는 ttysize 물건이 stdlib에 없다고 확신합니다.


물론, 내가 뭘하고 있어요 위해 ncurses를을 설정하는 정말 가치가 없다
오스틴

ncurses는 stdlib에도 없습니다. 둘 다 POSIX에서 표준화되었지만 ioctl저주 등을 초기화 할 필요가 없기 때문에 방법이 더 간단하고 깨끗합니다.
Gandaro

0

따라서 여기에 답변을 제안하지는 않지만 :

linux-pc:~/scratch$ echo $LINES

49

linux-pc:~/scratch$ printenv | grep LINES

linux-pc:~/scratch$

좋습니다. GNOME 터미널의 크기를 조정하면 LINES 및 COLUMNS 변수가 그 뒤에옵니다.

GNOME 터미널이 이러한 환경 변수 자체를 생성하는 것처럼 보입니다.


1
그리고 충분히 C 코드로 전달되지 않습니다. getenv ( "LINES")는 NULL을 반환합니다.
Scott Franco

변수는 터미널이 아니라 쉘의 것입니다.
melpomene

0

더 완전한 답변을 추가하기 위해 내가 찾은 것은 Rosetta Code 에서 추가 된 일부 비트와 함께 @John_T의 솔루션을 사용 하고 종속성을 파악하는 문제를 해결하는 것입니다. 약간 비효율적 일 수 있지만 스마트 프로그래밍을 사용하면 항상 터미널 파일을 열지 않고 작동하도록 만들 수 있습니다.

#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h> // ioctl, TIOCGWINSZ
#include <err.h>       // err
#include <fcntl.h>     // open
#include <unistd.h>    // close
#include <termios.h>   // don't remember, but it's needed

size_t* get_screen_size()
{
  size_t* result = malloc(sizeof(size_t) * 2);
  if(!result) err(1, "Memory Error");

  struct winsize ws;
  int fd;

  fd = open("/dev/tty", 0_RDWR);
  if(fd < 0 || ioctl(fd, TIOCGWINSZ, &ws) < 0) err(8, "/dev/tty");

  result[0] = ws.ws_row;
  result[1] = ws.ws_col;

  close(fd);

  return result;
}

모든 것을 호출하지 않고 가끔씩은 괜찮을 것입니다. 사용자가 터미널 창 크기를 조정할 때 업데이트해야합니다 (파일을 열고 매번 읽을 수 있기 때문입니다 ).

사용하지 않는 경우 https://www.linuxquestions.org/questions/programming-9/get-width-height-of-a-terminal-window-in-c-810739/TIOCGWINSZ 양식에서 첫 번째 답변을 참조하십시오 .

아, 그리고 잊지 마세요 .free()result


-1

다음은 이미 제안 된 환경 변수에 대한 함수 호출입니다.

int lines = atoi(getenv("LINES"));
int columns = atoi(getenv("COLUMNS"));

11
환경 변수는 신뢰할 수 없습니다. 이러한 값은 셸에서 설정하므로 존재가 보장되지 않습니다. 또한 사용자가 터미널 크기를 변경하면 최신 상태가 아닙니다.
Juliano 2009-06-21

1
많은 쉘은 SIGWINCH신호에 대한 핸들러를 설정 하므로 변수를 최신 상태로 유지할 수 있습니다 (입력 편집기에서 적절한 줄 바꿈을 수행 할 수 있도록 필요함).
Barmar

5
그들은 그렇게 할 수 있지만 프로그램의 환경은 실행 중일 때 업데이트되지 않습니다.
Functino 2015

물론이 코드는 getenv()NULL을 반환 하는지 여부를 테스트하지 않고 내 Linux 터미널에서 수행하기 때문에 충돌 할 가능성이 매우 높습니다 (변수가 내보내지지 않기 때문입니다.). 또한 쉘이 해당 변수를 업데이트하더라도 프로그램이 실행되는 동안 변경됩니다 (자체 SIGWINCH처리기가 없으면 안 됨 ).
Alexis Wilke
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.