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

bash input redirection breaks stdin

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

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

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

还有 tcsetattr returns -201 当它应该 return 0 或 -1.

我试图在 tcsetattr 之前用 scanf 清除 stdin 并完全禁用 tcsetattr 但结果输入重定向完全挂起所有输入。

没有输入重定向,一切正常,所以我想也许 bash 对 stdin 做了一些事情,所以它在程序中死机了。

有解决问题的想法吗?

errno 25 = "不适合设备的 ioctl";您正在对文件执行终端操作。您没有检查 tcgetattr() 中的 return。出错时,它设置 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 处终止。问题是 read() 没有明确指示 EOF——而是 return 为零——对于终端输入来说,这意味着没有字符。在那种情况下:

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