C如何从STDERR读取read()函数?

时间:2018-01-25 12:40:16

标签: c bash input pipe stderr

我得到了以下C代码:

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

int main(int argc, char **argv){
        char buf[10];
        read(2,buf,4);
}

据我所知,read()函数现在将从文件描述符2读取,这是Stderr。当我使用以下命令编译并执行它时:

gcc -o test test.c

在终端中,它会提示我输入类似于Stdin的输入。这是为什么?如何在bash中使用管道将stderr发送到文件? 提前致谢。

2 个答案:

答案 0 :(得分:1)

因为当你直接从命令行运行程序时,它的stdin和stderr都连接到同一个文件(你的终端)它有读取权限 。所以它只是从终端读取,但不一定是它的stderr。当你重定向它的stderr时,一切都失败了。

这是我的考验。

ibug@linux:~ $ cat t.c
#include <stdio.h>
#include <unistd.h>

int main(void){
        char b[10];
        printf("%ld\n", read(2, b, 10));
        return 0;
}
ibug@linux:~ $ gcc t.c
ibug@linux:~ $ ./a.out
abcdefg
8
ibug@linux:~ $ ./a.out 2>/dev/null
-1
ibug@linux:~ $ ./a.out 2>/dev/zero
-1
ibug@linux:~ $

答案 1 :(得分:1)

文件描述符

基于Unix的系统的许多历史怪癖之一是打开终端以供会话使用的代码通常像这样工作(实际上,忽略错误检查):

close(0);
open(tty_name, O_RDWR);
close(1);
dup(0);
close(2);
dup(0);
…
execv(shell[0], shell);

成功时,open()系统调用返回最低可用文件描述符。关闭文件描述符0后,后续打开打开文件描述符0(标准输入),打开终端进行读写。然后,从读/写描述符0复制文件描述符1(标准输出)和2(标准错误)。因此,默认情况下,所有三个标准文件描述符都是打开的,用于读取和写入。请注意术语&#39;文件描述符&#39;!

的使用

因此,程序通常可以写入文件描述符0并从文件描述符1或2(或两者)读取。如果您正在使用文件流(stdinstdoutstderr),则可能会遇到问题,但基础文件描述符通常是可读写的。重新连接到终端。

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

static void check_fd(int fd)
{
    char buffer[1024];

    err_remark("About to read from fd = %d\n", fd);
    ssize_t nbytes = read(fd, buffer, sizeof(buffer));
    if (nbytes < 0)
        err_sysrem("Failed to read fd = %d: ", fd);
    else if (nbytes == 0)
        err_remark("Got EOF (0 bytes read) on fd = %d\n", fd);
    else
    {
        err_remark("Got normal read of %d bytes on fd = %d\n", (int)nbytes, fd);
        printf("Data: [%.*s]\n", (int)nbytes - 1, buffer);
    }

    err_remark("About to write to fd = %d\n", fd);
    char message[] = "  'Twas brillig and the the slithy toves\n"
                     "  Did gyre and gimble in the wabe.\n"
                     "  All mimsy were the borogroves,\n"
                     "  And the mome raths outgrabe.\n";
    nbytes = write(fd, message, sizeof(message) - 1);
    if (nbytes < 0)
        err_sysrem("Failed to write to fd = %d: ", fd);
    else if (nbytes == (ssize_t)sizeof(message) - 1)
        err_remark("Successfully wrote %d bytes to fd = %d\n", (int)nbytes, fd);
    else
        err_remark("Got a short write (%d bytes written; %d expected) on fd = %d\n",
                   (int)nbytes, (int)(sizeof(message) - 1), fd);
}

int main(int argc, char **argv)
{
    if (argc != 0)
        err_setarg0(argv[0]);

    check_fd(STDIN_FILENO);
    check_fd(STDOUT_FILENO);
    check_fd(STDERR_FILENO);
    return 0;
}

此代码使用我首选的错误报告功能,即 可以在我的SOQ GitHub上找到(Stack 溢出问题)存储库作为文件stderr.cstderr.h src/libsoq 子目录。

样品运行:

$ ./stdio11
stdio11: About to read from fd = 0
This is typed at the terminal.
stdio11: Got normal read of 31 bytes on fd = 0
Data: [This is typed at the terminal.]
stdio11: About to write to fd = 0
  'Twas brillig and the the slithy toves
  Did gyre and gimble in the wabe.
  All mimsy were the borogroves,
  And the mome raths outgrabe.
stdio11: Successfully wrote 140 bytes to fd = 0
stdio11: About to read from fd = 1
More terminal typing.
stdio11: Got normal read of 22 bytes on fd = 1
Data: [More terminal typing.]
stdio11: About to write to fd = 1
  'Twas brillig and the the slithy toves
  Did gyre and gimble in the wabe.
  All mimsy were the borogroves,
  And the mome raths outgrabe.
stdio11: Successfully wrote 140 bytes to fd = 1
stdio11: About to read from fd = 2
The last line of input from the terminal for this process.
stdio11: Got normal read of 59 bytes on fd = 2
Data: [The last line of input from the terminal for this process.]
stdio11: About to write to fd = 2
  'Twas brillig and the the slithy toves
  Did gyre and gimble in the wabe.
  All mimsy were the borogroves,
  And the mome raths outgrabe.
stdio11: Successfully wrote 140 bytes to fd = 2
$

$ ./stdio11 </dev/null >output
stdio11: About to read from fd = 0
stdio11: Got EOF (0 bytes read) on fd = 0
stdio11: About to write to fd = 0
stdio11: Failed to write to fd = 0: error (9) Bad file descriptor
stdio11: About to read from fd = 1
stdio11: Failed to read fd = 1: error (9) Bad file descriptor
stdio11: About to write to fd = 1
stdio11: Successfully wrote 140 bytes to fd = 1
stdio11: About to read from fd = 2
Standard error is still the terminal, so input can still occur here.
stdio11: Got normal read of 69 bytes on fd = 2
stdio11: About to write to fd = 2
  'Twas brillig and the the slithy toves
  Did gyre and gimble in the wabe.
  All mimsy were the borogroves,
  And the mome raths outgrabe.
stdio11: Successfully wrote 140 bytes to fd = 2
$ cat output
  'Twas brillig and the the slithy toves
  Did gyre and gimble in the wabe.
  All mimsy were the borogroves,
  And the mome raths outgrabe.
Data: [Standard error is still the terminal, so input can still occur here.]
$

JFTR:上面的测试是在运行macOS High Sierra 10.13.3(并使用GCC 7.2.0)的MacBook Pro上完成的,但我希望在任何基于Unix的机器上都能获得相同的结果。

文件流

标准I / O文件流使用文件描述符0,1,2但它们未配置为允许stdin上的输出或stdoutstderr上的输入 - 鉴于流的名称,这是你所期望的。

以下是使用文件流(FILE *)而不是描述符的类似测试代码:

/* SO 4844-3136 - file streams version A */
#include <unistd.h>
#include "stderr.h"

static void check_fp(FILE *fp, const char *name)
{
    char buffer[1024];

    err_remark("About to read from %s\n", name);
    size_t nbytes = fread(buffer, sizeof(char), sizeof(buffer), fp);
    if (nbytes <= 0)
        err_sysrem("Failed to read %s: ", name);
    else
    {
        err_remark("Got normal read of %zu bytes on %s\n", nbytes, name);
        printf("Data: [%.*s]\n", (int)nbytes - 1, buffer);
    }

    err_remark("About to write to %s\n", name);
    char message[] = "  'Twas brillig and the the slithy toves\n"
                     "  Did gyre and gimble in the wabe.\n"
                     "  All mimsy were the borogroves,\n"
                     "  And the mome raths outgrabe.\n";
    nbytes = fwrite(message, sizeof(char), sizeof(message) - 1, fp);
    if (nbytes == 0)
        err_sysrem("Failed to write to %s: ", name);
    else if (nbytes == sizeof(message) - 1)
        err_remark("Successfully wrote %d bytes to %s\n", (int)nbytes, name);
    else
        err_remark("Got a short write (%zu bytes written; %zu expected) on %s\n",
                   nbytes, (sizeof(message) - 1), name);
}

int main(int argc, char **argv)
{
    if (argc != 0)
        err_setarg0(argv[0]);

    check_fp(stdin,  "stdin");
    check_fp(stdout, "stdout");
    check_fp(stderr, "stderr");
    return 0;
}

示例输出:

$  stdio13
stdio13: About to read from stdin
Hello, and welcome to the wonderful world of Unix.
Interestingly, the fread() function does not return when 
the input reaches the end of a line.  It continues reading
until the total amount of data entered, newlines and all,
is longer than the buffer it is given to read.  That is 
quite surprising in many ways.  However, such is life.
The test was run on a Mac with macOS 10.13.3, using GCC
7.3.0, which was released today, Friday 2018-01-26.  The
build on macOS was interesting; it had to be restarted
multiple times because headers were missing (when running
"make -j8").  Maybe the parallelism confused it?  Who knows!
It is hard to tell.  There are new versions of MPC and MPFR
that can be used, too.  Grump!  It takes a while to type as
much as 1024 bytes of data.  Let's see: at 64 characters
per line, that would be 16 lines.  And this is line 15.
We should soon be done - though the lines may not all be as
long as 64 bytes.  In fact, none of them is 64 bytes long.
But they're close to 60 bytes each, so it won't take much
stdio13: Got normal read of 1024 bytes on stdin
Data: [Hello, and welcome to the wonderful world of Unix.
Interestingly, the fread() function does not return when
the input reaches the end of a line.  It continues reading
until the total amount of data entered, newlines and all,
is longer than the buffer it is given to read.  That is
quite surprising in many ways.  However, such is life.
The test was run on a Mac with macOS 10.13.3, using GCC
7.3.0, which was released today, Friday 2018-01-26.  The
build on macOS was interesting; it had to be restarted
multiple times because headers were missing (when running
"make -j8").  Maybe the parallelism confused it?  Who knows!
It is hard to tell.  There are new versions of MPC and MPFR
that can be used, too.  Grump!  It takes a while to type as
much as 1024 bytes of data.  Let's see: at 64 characters
per line, that would be 16 lines.  And this is line 15.
We should soon be done - though the lines may not all be as
long as 64 bytes.  In fact, none of them is 64 bytes long.
But they're close to 60 bytes each, so it won't ]
stdio13: About to write to stdin
stdio13: Failed to write to stdin: error (9) Bad file descriptor
stdio13: About to read from stdout
stdio13: Failed to read stdout: error (9) Bad file descriptor
stdio13: About to write to stdout
  'Twas brillig and the the slithy toves
  Did gyre and gimble in the wabe.
  All mimsy were the borogroves,
  And the mome raths outgrabe.
stdio13: Successfully wrote 140 bytes to stdout
stdio13: About to read from stderr
stdio13: Failed to read stderr: error (9) Bad file descriptor
stdio13: About to write to stderr
  'Twas brillig and the the slithy toves
  Did gyre and gimble in the wabe.
  All mimsy were the borogroves,
  And the mome raths outgrabe.
stdio13: Successfully wrote 140 bytes to stderr
$

这清楚地表明,您无法写信至stdin或从stdoutstderr阅读,至少在Mac上。

您可以在我的SOQ(Stack。)中的GitHub上找到此代码 溢出问题)存储库中 src/so-4844-3136 子目录。还有一个使用stdio17.cfgets()的变体fputs(),但在某些方面不太令人满意,因为I / O功能不报告长度输入或输出与fread()fwrite()不同,但fgets()在第一个换行符处停止读取,与fread()不同。