【问题标题】:Getting terminal width in C?在C中获取终端宽度?
【发布时间】:2010-11-04 14:38:09
【问题描述】:

我一直在寻找一种从我的 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;
}

【问题讨论】:

  • 所有建议的答案都不对一半以上。
  • @ThomasDickey,那你的答案在哪里?

标签: c linux terminal width


【解决方案1】:

要添加更完整的答案,我发现对我有用的是使用@John_T 的解决方案,其中添加了来自Rosetta Code 的一些位,以及一些排除依赖关系的故障排除。这可能有点效率低下,但通过智能编程,您可以让它工作,而不是一直打开终端文件。

#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;
}

如果你确保不要全部调用它,但也许每隔一段时间你应该没问题,它甚至应该在用户调整终端窗口大小时更新(因为你正在打开文件并阅读它每个 时间)。

如果您不使用TIOCGWINSZ,请参阅此表单上的第一个答案https://www.linuxquestions.org/questions/programming-9/get-width-height-of-a-terminal-window-in-c-810739/

哦,别忘了free()result

【讨论】:

  • 你为什么要返回size_t *?只需为此创建一个结构并返回结构值。这样就没有malloc()free() 参与。
  • 对于使用此代码的人:在对 open 的调用中的 0_RDWR 必须是 O_RDWR(O 插入为 0)。
【解决方案2】:

所以这里不建议答案,但是:

linux-pc:~/scratch$ echo $LINES

49

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

linux-pc:~/scratch$

好的,我注意到如果我调整 GNOME 终端的大小,LINES 和 COLUMNS 变量会随之而来。

有点像 GNOME 终端自己创建这些环境变量?

【讨论】:

  • 果然它不会传递给 C 代码。 getenv("LINES") 返回 NULL。
  • 变量是外壳,不是终端。
【解决方案3】:

您是否考虑过使用 getenv() ?它允许您获取包含终端列和行的系统环境变量。

或者使用您的方法,如果您想查看内核看到的终端大小(如果终端调整大小更好),您需要使用 TIOCGWINSZ,而不是您的 TIOCGSIZE,如下所示:

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
}

【讨论】:

  • 是的,但是术语宽度不是环境变量,它对术语来说是静态的。
  • 如果有人在程序执行期间调整终端大小,它不会为您提供 当前 终端大小。
  • @Debashish 取决于。例如。 Linux 根本不支持这些字段。
  • 虽然我们可以看到环境中的LINESCOLUMNS变量,但它们似乎没有被导出。因此无法从您的 C 程序访问。
  • 刚刚偶然发现了这个答案,当我意识到getenv("COLUMNS") 在监视下运行时完美运行时,我的下巴掉了下来(1)。所以现在我有一组后备,从TIOCWINSZ ioctl,到 getenv,如果不是 tty,再到经典的 ANSI 转义“将光标移动到 9999,9999 和查询光标 pos。最后一个适用于串行控制台对于嵌入式系统:)
【解决方案4】:

这个例子有点冗长,但我相信它是检测终端尺寸的最便携的方法。这也处理调整大小事件。

正如 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);
}

【讨论】:

  • 但是从信号处理程序调用 initscr 和 endwin 真的安全吗?它们至少没有在 man 7 signal 中的异步信号安全 API 中列出
  • 这是一个很好的观点@nav,我从来没有想过这个!更好的解决方案可能是让信号处理程序引发一个标志,然后在主循环中执行其余操作?
  • @gamen,是的,那会更好;) - 使用 sigaction 代替信号也会更好。
  • COLS 和 LINES 也是全局变量吗?
  • @AlexisWilke:包括OKERR。他们多么“友善”,可以帮助我们填补生活中的空白:-(
【解决方案5】:

假设你在 Linux 上,我想你想使用 ncurses 库 反而。我很确定您拥有的 ttysize 内容不在 stdlib 中。

【讨论】:

  • 好吧,我所做的并不值得为它设置 ncurses
  • ncurses 也不在标准库中。两者在 POSIX 中都是标准化的,但 ioctl 方式更简单、更干净,因为您不必初始化 curses 等。
【解决方案6】:
#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编译它。
  • 我知道这条评论是在事实发生 6 年后发表的,但请解释一下你的神奇数字 2048...
  • @einpoklum 这已经是将近三年后了,但不是很清楚 2048 只是缓冲区的任意大小,对于任何输入字符串“应该足够大”到那里?
  • 其实这个答案做了太多假设是正确的。
  • 对于任何好奇的人,2048 缓冲区大小在此处的 GNU termcap 文档中进行了说明:gnu.org/software/termutils/manual/termcap-1.3/html_mono/… 那里还有很多其他内容,阅读这篇文章的人可能会觉得有用。
【解决方案7】:

这里是已经建议的环境变量事物的函数调用:

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

【讨论】:

  • 环境变量不可靠。这些值由 shell 设置,因此不能保证它们存在。此外,如果用户更改终端尺寸,它们也不会是最新的。
  • 许多 shell 为SIGWINCH 信号建立了一个处理程序,因此它们可以使变量保持最新(它们也需要它,以便在输入编辑器中进行正确的换行)。跨度>
  • 他们可能会这样做,但程序的环境不会在运行时更新。
  • 当然,该代码很可能会崩溃,因为您不测试 getenv() 是否返回 NULL 并且它在我的 Linux 终端中确实如此(因为没有导出这些变量。)此外,即使shell 会更新这些变量,您在程序运行时不会看到更改(如果没有您自己的 SIGWINCH 处理程序,则不会)。
【解决方案8】:

如果您已安装并正在使用 ncurses,您可以使用getmaxyx() 查找终端的尺寸。

【讨论】:

  • 是的,请注意先是 Y,然后是 X。
猜你喜欢
  • 2012-09-30
  • 2015-07-31
  • 2015-04-09
  • 2012-01-21
  • 2011-01-05
  • 1970-01-01
  • 2018-10-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多