我得到了以下C代码:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char **argv){
char buf[10];
read(2,buf,4);
}
据我所知,read() 函数现在将从 Stderr 的文件描述符 2 中读取。当我使用以下命令编译并执行它时:
gcc -o test test.c
在终端中,它会提示我像输入 Stdin 一样键入输入。为什么?我如何使用 bash 中的管道将 stderr 发送到文件? 提前谢谢。
因为当您直接从命令行运行程序时,它的 stdin 和 stderr 都连接到它具有读取访问权限的同一文件(您的终端)。因此,它只是从终端读取,但不一定是其标准。当您重定向其标准时,一切都会失败。
这是我的测试。
ibug@linux:~ $ cat t.c
#include <stdio.h>
#include <unistd.h>
int main(void){
char b[10];
printf("%ldn", 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:~ $
文件描述符
基于 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(标准错误)。 因此,默认情况下,所有三个标准文件描述符都对读取和写入都是开放的。 请注意术语"文件描述符"的使用!
因此,程序通常可以写入文件描述符 0 并从文件描述符 1 或 2(或两者)读取。 如果您使用的是文件流(stdin
、stdout
、stderr
),您可能会遇到问题,但底层文件描述符在连接到终端时通常是可读写的。
#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 = %dn", 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 = %dn", fd);
else
{
err_remark("Got normal read of %d bytes on fd = %dn", (int)nbytes, fd);
printf("Data: [%.*s]n", (int)nbytes - 1, buffer);
}
err_remark("About to write to fd = %dn", fd);
char message[] = " 'Twas brillig and the the slithy tovesn"
" 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 = %dn", (int)nbytes, fd);
else
err_remark("Got a short write (%d bytes written; %d expected) on fd = %dn",
(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 上可用(堆栈 溢出问题)存储库作为文件stderr.c
stderr.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
上输出或在stdout
或stderr
上输入 — 这是您在给定流名称时所期望的。
以下是使用文件流(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 %sn", 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 %sn", nbytes, name);
printf("Data: [%.*s]n", (int)nbytes - 1, buffer);
}
err_remark("About to write to %sn", name);
char message[] = " 'Twas brillig and the the slithy tovesn"
" 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 %sn", (int)nbytes, name);
else
err_remark("Got a short write (%zu bytes written; %zu expected) on %sn",
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
$
这清楚地表明,您至少在Mac上无法写入stdin
或从stdout
或stderr
中读取
。你可以在我的SOQ(堆栈)的GitHub上找到这段代码。 溢出问题)存储库中的 SRC/SO-4844-3136 子目录。 还有一个变体stdio17.c
使用fgets()
和fputs()
,但这在某些方面不太令人满意,因为 I/O 函数不报告输入或输出的长度,不像fread()
和fwrite()
- 但fgets()
在第一个换行符处停止读取,不像fread()
。