2014-11-05 75 views
3

我想知道如果我的输入被重定向,应该如何去执行C程序中的操作。例如,说我有我编译的程序“编”,我重定向输入“input.txt”它(我做./prog < input.txt)。如果输入被重定向,则执行操作

如何在代码中检测到这一点?

回答

6

一般来说,您不能判断输入是否已被重定向;但是你可以根据stdin是什么类型的文件来区分。如果没有重定向,它将是一个终端;或者它可能被设置为管道cat foo | ./prog,或者从常规文件重定向(如您的示例),或者从多种类型的特殊文件之一重定向(./prog </dev/sda1将其从块特殊文件重定向等) 。

所以,如果你想确定标准输入是终端(TTY),你可以使用isatty

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

int main(int argc, char **argv) { 
    if (isatty(STDIN_FILENO)) 
     printf("stdin is a tty\n"); 
    else 
     printf("stdin is not a tty\n"); 

    return 0; 
} 

如果你想其他情况下(如管道,块特殊文件,等来区分),您可以使用fstat来提取更多的文件类型信息。请注意,这实际上并不告诉你它是否是一个终端,您仍然需要isatty(这是一个围绕ioctl提供有关终端信息的包装,至少在Linux上)。您可以将以下内容添加到上述程序(以及#include <sys/stat.h>)以获取有关哪种文件标准输入是附加信息。

if (fstat(STDIN_FILENO, &sb) == 0) { 
    if (S_ISBLK(sb.st_mode)) 
     printf("stdin is a block special file\n"); 
    else if (S_ISCHR(sb.st_mode)) 
     printf("stdin is a character special file\n"); 
    else if (S_ISDIR(sb.st_mode)) 
     printf("stdin is a directory\n"); 
    else if (S_ISFIFO(sb.st_mode)) 
     printf("stdin is a FIFO (pipe)\n"); 
    else if (S_ISREG(sb.st_mode)) 
     printf("stdin is a regular file\n"); 
    else if (S_ISLNK(sb.st_mode)) 
     printf("stdin is a symlink\n"); 
    else if (S_ISSOCK(sb.st_mode)) 
     printf("stdin is a socket\n"); 
} else { 
    printf("failed to stat stdin\n"); 
} 

请注意,你将永远不会看到重定向符号链接,作为外壳将已经代表你的程序中打开文件之前取消引用的符号链接;而且我也无法让Bash打开一个Unix域套接字。但其余的都是可能的,包括令人惊讶的是,一个目录。

$ ./isatty 
stdin is a tty 
stdin is a character special file 
$ ./isatty < ./isatty 
stdin is not a tty 
stdin is a regular file 
$ sudo sh -c './isatty < /dev/sda' 
stdin is not a tty 
stdin is a block special file 
$ sudo sh -c './isatty < /dev/console' 
stdin is a tty 
stdin is a character special file 
$ cat isatty | ./isatty 
stdin is not a tty 
stdin is a FIFO (pipe) 
$ mkfifo fifo 
$ echo > fifo & # Need to do this or else opening the fifo for read will block 
[1] 27931 
$ ./isatty < fifo 
stdin is not a tty 
stdin is a FIFO (pipe) 
[1]+ Done     echo > fifo 
$ ./isatty < . 
stdin is not a tty 
stdin is a directory 
$ socat /dev/null UNIX-LISTEN:./unix-socket & 
[1] 28044 
$ ./isatty < ./unix-socket 
bash: ./unix-socket: No such device or address 
$ kill $! 
[1]+ Exit 143    socat /dev/null UNIX-LISTEN:./unix-socket 
$ ln -s isatty symlink 
$ ./isatty < symlink 
stdin is not a tty 
stdin is a regular file 
$ ln -s no-such-file broken-link 
$ ./isatty < broken-link 
bash: broken-link: No such file or directory 
相关问题