C 以安全、便携的方式使stdin可写
我试着运行两个程序 更新之前 在第一个程序中,3个空字节和一个换行符可能不一定按该顺序写入屏幕;然后,假定命令行上没有I/O重定向,程序尝试从键盘读取数据。写入标准输入不会加载输入缓冲区。您通常可以写入标准输入,读取标准输出和标准错误,因为经典技术使用O_RDWR打开文件描述符,然后将其连接到标准I/O通道。但是,不能保证您可以这样做。顺便说一句,第一个项目需要 第二个程序有太多未定义的行为,很难分析。open调用需要三个参数,因为它包括O_CREAT;第三个参数是文件的模式,例如0644。您没有检查打开是否成功。您不检查写入是否成功;它不会,因为文件描述符是只读打开的,或者说源fd是只读打开的,dup会将该模式复制到文件描述符0,这意味着写入将失败。未检查输入操作,无法确保scanf成功。第二个程序实际上不需要或 基本上,您不知道发生了什么,因为您没有检查任何关键函数调用 更新后1 请注意,错误消息应写入标准错误,并应以换行符终止 我让第一个程序按照MacOSX10.10Yosemite,GCC4.8.1的规定工作,尽管很难证明空字节写入了标准输入,但在那里写了一个换行符。然后我可以输入10或20,或100,或…加上Return,然后这个整数就会被打印出来 第二个程序在scanf上失败,因为当您尝试读取时,文件指针位于文件的末尾。您可以通过此程序变体看到:C 以安全、便携的方式使stdin可写,c,stdin,portability,C,Stdin,Portability,我试着运行两个程序 更新之前 在第一个程序中,3个空字节和一个换行符可能不一定按该顺序写入屏幕;然后,假定命令行上没有I/O重定向,程序尝试从键盘读取数据。写入标准输入不会加载输入缓冲区。您通常可以写入标准输入,读取标准输出和标准错误,因为经典技术使用O_RDWR打开文件描述符,然后将其连接到标准I/O通道。但是,不能保证您可以这样做。顺便说一句,第一个项目需要 第二个程序有太多未定义的行为,很难分析。open调用需要三个参数,因为它包括O_CREAT;第三个参数是文件的模式,例如0644。您没
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main(void)
{
/* Open file from which content shall be inserted to stdin_buffer */
int source_fd = open ("file_in.txt", O_CREAT | O_RDWR, S_IRWXU);
if (-1 == source_fd)
{
printf ("Failed to open file for reading\n");
exit (EXIT_FAILURE);
}
close(0);
int stdin_fd = dup (source_fd);
if (-1 == stdin_fd)
{
printf("Failed to dup\n");
exit(EXIT_FAILURE);
}
int k = 10;
int ret_val = write(stdin_fd, &k, sizeof(int));
if (-1 == ret_val)
{
printf("Failed to write to stdin_buffer\n");
exit(EXIT_FAILURE);
}
int rc;
int n;
if ((rc = scanf("%d", &n)) != 1)
printf("Failed to read from standard input: rc = %d\n", rc);
else
printf("Integer read is %d (0x%08x)\n", n, n);
close(source_fd);
return 0;
}
如果在读取之前倒带文件,则返回0;写入文件的二进制数据不是整数的有效字符串表示形式
更新后2
我编写了一个小函数err_exit,因为它允许代码在页面上更小。我在几个地方修改了您的代码,以报告来自上一个函数的返回值。缺少输入不是错误。当读取0字节时,这不是错误;这是EOF。如果有数据要读取,但不是整数值的文本格式,则不会进行转换,但这不是错误
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#define LEN 100
static void err_exit(const char *msg)
{
perror(msg);
exit(EXIT_FAILURE);
}
int main(void)
{
int n = 99;
char buffer[LEN];
memset(buffer, '\0', LEN);
int ret_val = 0;
/* Open file from which content shall be inserted to stdin_buffer */
int source_fd = open("file_in.txt", O_CREAT | O_RDONLY, S_IRWXU);
if (-1 == source_fd)
err_exit("Failed to open file for reading");
/* Temp stdin_buffer */
int temp_fd = open("temp_in.txt", O_CREAT | O_RDWR, S_IRWXU);
if (-1 == temp_fd)
err_exit("Failed to open temp stdin");
/* Close STDIN_FILENO */
close(0);
/* dup the source */
int stdin_fd = dup(temp_fd);
if (-1 == stdin_fd)
err_exit("Failed to dup");
ret_val = read(source_fd, buffer, LEN);
if (-1 == ret_val)
err_exit("Failed to read from source");
else
printf("(%d bytes) <<%s>> read from Source file\n", ret_val, buffer);
/* write to stdin_buffer (content taken from file_in.txt) */
ret_val = write(stdin_fd, buffer, LEN);
if (-1 == ret_val)
err_exit("Failed to write to stdin_buffer");
ret_val = lseek(stdin_fd, 0, SEEK_SET);
if (-1 == ret_val)
err_exit("Failed lseek");
ret_val = scanf("%d", &n);
if (-1 == ret_val)
err_exit("Failed to read stdin_buffer");
printf("Integer read is %d (ret_val = %d)\n", n, ret_val);
close(source_fd);
return 0;
}
输出:
(0 bytes) <<>> read from Source file
Integer read is 99 (ret_val = 0)
(0 bytes) <<>> read from Source file
Integer read is 10 (ret_val = 1)
让我们看看你的三个程序在做什么 方案1 第一个程序写入文件描述符0。默认情况下,这是stdout,因此宏stdout_FILENO具有该值。由于stdout是一个单向流,运行到进程中,因此写入失败 方案2 在这个程序中,您打开一个文件,对于标准流,它可能在0-2之后获得FD filedescriptor 3。然后,用fd0关闭stdout。然后,复制fd3,因为第一个打开点位于索引0处,所以这是文件的新FD。由于scanf只使用未更改的宏STDIN_FILENO,因此它将在那里拾取该文件的内容 方案3 在程序3中,您的操作与程序2中的操作几乎相同,只是打开了两次文件。对正在发生的事情的解释基本上遵循了上述两个方面 现在的问题是,当你们说一个更好的工作代码时,你们的意思是什么。关键是,除非你具体说明你真正想要什么,否则不可能说得更好。根据您的主题和注释猜测,您希望远程控制第二个进程,就像在bash中使用管道符号一样 为了做到这一点,你将不得不求助于操作系统特定的手段,只要搜索输入输出重定向,你应该能够找到一些信息。既然您已经提到了execl,那么POSIX之外的可移植性(例如到win32)似乎不是一个问题,因此您应该可以在那里找到很多示例代码,比我在这里所写的要好得多 在任何情况下,大致可归结为以下步骤: 创建一对FD的管道 fork创建一个新流程 子:dup2将输入FD从管道重新分配到标准输入 子进程:执行新进程 父进程:写入输出FD以生成子进程的输入
此外,您应该关闭未使用的流,或者使用fcntlFD_CLOEXEC对其进行配置,使其自动关闭。重要的一点是,两个FD保持连接到同一个FD!通道,尽管在两个进程中都有两端。关闭每个进程的一端会在两个进程之间留下一个单向通道。这是操作系统特有的。在Linux上,尝试删除程序。是特定于操作系统还是依赖于编译器?那么,是否有一种可移植的方式来写入stdin?编译器只是从libstdc++或libc调用函数。而且也没有便携的方式来实现w
向斯丁致敬。你的代码不会做你认为它能做的事。在Linux上,阅读关于您的第一个程序缺少一些include指令的信息。你为什么要写stdin?@KeithThompson我试图用execl运行一个可执行文件。在我完成这项工作之后,我想在执行过程中为它提供输入,就像我们在在线编码平台中所做的那样。我的想法是将输入写入一个文件并将其重定向到stdin。我已经更改了代码。请仔细阅读并给出您的反馈。我已经用一个新的代码片段更新了这个问题。我相信这在各方面都是正确的。
Failed to read from standard input: rc = -1
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#define LEN 100
static void err_exit(const char *msg)
{
perror(msg);
exit(EXIT_FAILURE);
}
int main(void)
{
int n = 99;
char buffer[LEN];
memset(buffer, '\0', LEN);
int ret_val = 0;
/* Open file from which content shall be inserted to stdin_buffer */
int source_fd = open("file_in.txt", O_CREAT | O_RDONLY, S_IRWXU);
if (-1 == source_fd)
err_exit("Failed to open file for reading");
/* Temp stdin_buffer */
int temp_fd = open("temp_in.txt", O_CREAT | O_RDWR, S_IRWXU);
if (-1 == temp_fd)
err_exit("Failed to open temp stdin");
/* Close STDIN_FILENO */
close(0);
/* dup the source */
int stdin_fd = dup(temp_fd);
if (-1 == stdin_fd)
err_exit("Failed to dup");
ret_val = read(source_fd, buffer, LEN);
if (-1 == ret_val)
err_exit("Failed to read from source");
else
printf("(%d bytes) <<%s>> read from Source file\n", ret_val, buffer);
/* write to stdin_buffer (content taken from file_in.txt) */
ret_val = write(stdin_fd, buffer, LEN);
if (-1 == ret_val)
err_exit("Failed to write to stdin_buffer");
ret_val = lseek(stdin_fd, 0, SEEK_SET);
if (-1 == ret_val)
err_exit("Failed lseek");
ret_val = scanf("%d", &n);
if (-1 == ret_val)
err_exit("Failed to read stdin_buffer");
printf("Integer read is %d (ret_val = %d)\n", n, ret_val);
close(source_fd);
return 0;
}
(0 bytes) <<>> read from Source file
Integer read is 99 (ret_val = 0)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#define LEN 100
static void err_exit(const char *msg)
{
perror(msg);
exit(EXIT_FAILURE);
}
int main(void)
{
int n = 99;
char buffer[LEN] = "";
int ret_val = 0;
/* Open file from which content shall be inserted to stdin */
int source_fd = open("file_in.txt", O_CREAT | O_RDONLY, S_IRWXU);
if (-1 == source_fd)
err_exit("Failed to open file for reading");
/* Temp stdin */
int temp_fd = open("temp_in.txt", O_CREAT | O_RDWR, S_IRWXU);
if (-1 == temp_fd)
err_exit("Failed to open temp stdin");
/* Close STDIN_FILENO */
close(0);
/* dup the source */
int stdin_fd = dup(temp_fd);
if (-1 == stdin_fd)
err_exit("Failed to dup");
ret_val = read(source_fd, buffer, LEN);
if (-1 == ret_val)
err_exit("Failed to read from source");
else
printf("(%d bytes) <<%s>> read from Source file\n", ret_val, buffer);
/* write to stdin (content taken from file_in.txt) */
ret_val = write(stdin_fd, "10\n", sizeof("10\n")-1);
if (-1 == ret_val)
err_exit("Failed to write to stdin");
ret_val = lseek(stdin_fd, 0, SEEK_SET);
if (-1 == ret_val)
err_exit("Failed lseek");
ret_val = scanf("%d", &n);
if (-1 == ret_val)
err_exit("Failed to read stdin");
printf("Integer read is %d (ret_val = %d)\n", n, ret_val);
close(source_fd);
return 0;
}
(0 bytes) <<>> read from Source file
Integer read is 10 (ret_val = 1)