在管道传输到标准输入时使用 ioctl 填充 winsize 结构
using ioctl to fill a winsize struct when piping to stdin
我正在尝试使用 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 指的不是终端而是管道。管道没有 window 尺寸。这就是 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()
检索终端的宽度,但在管道或重定向到标准输入时它不起作用。
我已经通过解析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 指的不是终端而是管道。管道没有 window 尺寸。这就是 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);