管道到标准输入时使用ioctl填充winsize结构

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了管道到标准输入时使用ioctl填充winsize结构相关的知识,希望对你有一定的参考价值。

我正在尝试使用ioctl()检索终端的宽度,但在管道或重定向到标准输入时它不起作用。

我设法通过解析tput cols的结果来绕过这个问题,但是使用外部命令感觉很脏。此外,我认为这使得它不太便携,因为Windows不使用与bourne兼容的shell?

main.c中

// tput method
char res[10];

FILE cmd = popen("tput cols", "r");
fgets(res, 10 - 1, cmd);
pclose(cmd);

unsigned short term_cols = atoi(res);
printf("Term width (tput): %d\n", term_cols);

// ioctl method
struct winsize ws;
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == 0)
{
  printf("Term width (ioctl): %d\n", ws.ws_col);
}
else
{
  printf("Failed to retrieve term width from ioctl()");
}

产量

$ bin/main  
Term width (tput): 84  
Term width (ioctl): 84
$ echo "test" | bin/main  
Term width (tput): 84  
Failed to retrieve term width from ioctl()

我在代码的开头尝试过fflush(stdin);,但它没有任何区别。这只是ioctl()的限制还是有办法绕过它?

答案

您可能正在打印未初始化变量的值。你的代码不检查ioctl是否成功,如果失败,它会保持ws不受影响。

固定:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

...
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) {
    fprintf(stderr, "can't get the window size of stdin: %s\n", strerror(errno));
    exit(EXIT_FAILURE);
}

当您将某些东西输入程序时,stdin不会引用终端而是引用管道。管道没有窗口大小。这就是TIOCGWINSZ在这里失败的原因。

便携式解决方案似乎是:

const char *term = ctermid(NULL);
if (!term[0]) {
    fprintf(stderr, "can't get the name of my controlling terminal\n");
    exit(EXIT_FAILURE);
}
int fd = open(term, O_RDONLY);
if (fd == -1) {
    fprintf(stderr, "can't open my terminal at %s: %s\n", term, strerror(errno));
    exit(EXIT_FAILURE);
}
if (ioctl(fd, TIOCGWINSZ, &ws) == -1) {
    fprintf(stderr, "can't get the window size of %s: %s\n", term, strerror(errno));
    exit(EXIT_FAILURE);
}
close(fd);

以上是关于管道到标准输入时使用ioctl填充winsize结构的主要内容,如果未能解决你的问题,请参考以下文章

cin.tellg 从管道接收输入时返回 -1

命令输出(stdout,stderr)未重定向到管道

使用带有标准输入和标准输出重定向的 2 进程管道时如何避免标准输入上的重复输入

如何使用 Python 将标准输入/标准输出通过管道传输到 Perl 脚本

aplay 管道使用文件而不是标准输入和标准输出来记录

标准输入输出和管道