bash 输入重定向破坏了标准输入

发布于 2025-01-13 21:29:31 字数 1079 浏览 2 评论 0 原文

我有一个看起来像这样的程序:

#include <stdio.h>
#include <unistd.h>
#include <termios.h>
#include <errno.h>

int enable_keyboard() {
    struct termios new_term_attr;

    tcgetattr(fileno(stdin), &new_term_attr);
    new_term_attr.c_lflag &= ~(ECHO|ICANON);
    new_term_attr.c_cc[VTIME] = 0;
    new_term_attr.c_cc[VMIN] = 0;
    return tcsetattr(fileno(stdin), TCSANOW, &new_term_attr);
}

int main() {
    errno = 0;
    unsigned char field[H][W];

    fill (field);
    char c = enable_keyboard();;
    while(1) {
        read(0, &c, 1);
        printf("%d ", c);
    }
}

它从标准输入读取单个字符并无限显示它(以检查enable_keybord是否正常工作)。
问题是,当我使用输入重定向(行 ./a.out )运行此程序时,它会破坏所有输入,并且 errno 设置为25.

另外,tcsetattr 在应该返回 0 或 -1 时却返回 -201。

我尝试在 tcsetattr 之前使用 scanf 清除 stdin 并完全禁用 tcsetattr 但事实证明输入重定向全部挂起输入完全。

如果没有输入重定向,一切都工作得很好,所以我想也许 bash 用 stdin 做了一些事情,所以它在程序中冻结了。

有什么想法如何解决这个问题吗?

I have a program that looks like this:

#include <stdio.h>
#include <unistd.h>
#include <termios.h>
#include <errno.h>

int enable_keyboard() {
    struct termios new_term_attr;

    tcgetattr(fileno(stdin), &new_term_attr);
    new_term_attr.c_lflag &= ~(ECHO|ICANON);
    new_term_attr.c_cc[VTIME] = 0;
    new_term_attr.c_cc[VMIN] = 0;
    return tcsetattr(fileno(stdin), TCSANOW, &new_term_attr);
}

int main() {
    errno = 0;
    unsigned char field[H][W];

    fill (field);
    char c = enable_keyboard();;
    while(1) {
        read(0, &c, 1);
        printf("%d ", c);
    }
}

It reads a single char from stdin and displays it infinitely(in order to check whether enable_keybord works fine).
The problem is that when I run this program with input redirection(line ./a.out < test.txt) it breaks all the input and errno is set to be 25.

Also tcsetattr returns -201 when it should return 0 or -1.

I tried to clear stdin with scanf before tcsetattr and also disabling tcsetattr completely but it turns out input redirection hangs all the input completely.

Without input redirection everything works perfectly fine so I guess maybe bash does something with stdin so it freezes dead in program.

Any ideas how to fix the problem?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

木緿 2025-01-20 21:29:31

errno 25 = "设备的 ioctl 不合适";您正在对文件执行终端操作。您不检查 tcgetattr() 的返回结果。出错时,它会设置 errno,如果文件描述符不代表终端,则设置为 ENOTTY。所以:

int enable_keyboard() 
{
    struct termios new_term_attr;
    int status = -1 ;

    if( tcgetattr(fileno(stdin), &new_term_attr) == 0 )
    {
        new_term_attr.c_lflag &= ~(ECHO|ICANON);
        new_term_attr.c_cc[VTIME] = 0;
        new_term_attr.c_cc[VMIN] = 0;
        status = tcsetattr(fileno(stdin), TCSANOW, &new_term_attr);
    }

    return status ;
}

这并不能解决你的“挂起”问题 - 正如你所提到的,你的程序无限循环 - while 循环不会在 EOF 上终止。问题是 EOF 没有由 read() 明确指示 - 相反它返回零 - 这对于终端输入来说仅仅意味着没有字符。在这种情况下:

char c = 0 ;
bool is_file = enable_keyboard() != 0 ;
bool terminate = false ;

while( !terminate ) 
{
    terminate = read(0, &c, 1) == 0 && is_file ;
    printf("%d ", c);
}

将所有这些放在一起:

#include <stdio.h>
#include <unistd.h>
#include <termios.h>
#include <errno.h>
#include <stdbool.h>

int enable_keyboard() 
{
    struct termios new_term_attr;
    int status = -1 ;

    if( tcgetattr(fileno(stdin), &new_term_attr) == 0 )
    {
        new_term_attr.c_lflag &= ~(ECHO|ICANON);
        new_term_attr.c_cc[VTIME] = 0;
        new_term_attr.c_cc[VMIN] = 0;
        status = tcsetattr(fileno(stdin), TCSANOW, &new_term_attr);
    }

    return status ;
}

int main() 
{
    errno = 0;

    char c = 0 ;
    bool is_file = enable_keyboard() != 0 ;
    bool terminate = false ;
    
    while( !terminate ) 
    {
        terminate = read(0, &c, 1) == 0 && is_file ;
        printf("%d ", c);
    }

    return 0 ;
}

errno 25 = "Inappropriate ioctl for device"; you are performing terminal operations on a file. You do not check the return from tcgetattr(). On error, it sets errno and that is set to ENOTTY if the file descriptor does not represent a terminal. So:

int enable_keyboard() 
{
    struct termios new_term_attr;
    int status = -1 ;

    if( tcgetattr(fileno(stdin), &new_term_attr) == 0 )
    {
        new_term_attr.c_lflag &= ~(ECHO|ICANON);
        new_term_attr.c_cc[VTIME] = 0;
        new_term_attr.c_cc[VMIN] = 0;
        status = tcsetattr(fileno(stdin), TCSANOW, &new_term_attr);
    }

    return status ;
}

That will not solve your "hanging" issue however - as you mentioned, your program loops indefinitely - the while loop does not terminate on EOF. The problem is that EOF is not explicitly indicated by read() - rather it returns zero - which for terminal input simply mean no characters. In that case:

char c = 0 ;
bool is_file = enable_keyboard() != 0 ;
bool terminate = false ;

while( !terminate ) 
{
    terminate = read(0, &c, 1) == 0 && is_file ;
    printf("%d ", c);
}

Putting that all together:

#include <stdio.h>
#include <unistd.h>
#include <termios.h>
#include <errno.h>
#include <stdbool.h>

int enable_keyboard() 
{
    struct termios new_term_attr;
    int status = -1 ;

    if( tcgetattr(fileno(stdin), &new_term_attr) == 0 )
    {
        new_term_attr.c_lflag &= ~(ECHO|ICANON);
        new_term_attr.c_cc[VTIME] = 0;
        new_term_attr.c_cc[VMIN] = 0;
        status = tcsetattr(fileno(stdin), TCSANOW, &new_term_attr);
    }

    return status ;
}

int main() 
{
    errno = 0;

    char c = 0 ;
    bool is_file = enable_keyboard() != 0 ;
    bool terminate = false ;
    
    while( !terminate ) 
    {
        terminate = read(0, &c, 1) == 0 && is_file ;
        printf("%d ", c);
    }

    return 0 ;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文