如果重定向输入,则执行操作

时间:2014-11-05 00:18:49

标签: c linux

如果我的输入被重定向,我想知道如何在C程序中执行操作。例如,假设我编译了程序“prog”,并将输入“input.txt”重定向到它(我做./prog < input.txt)。

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

1 个答案:

答案 0 :(得分:7)

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

因此,如果您想确定stdin是否是终端(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>)中,以获取有关stdin文件类型的更多信息。

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

请注意,您永远不会看到重定向的符号链接,因为在代表您的程序打开文件之前,shell已经取消引用了符号链接;我也无法让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