C 以安全、便携的方式使stdin可写

C 以安全、便携的方式使stdin可写,c,stdin,portability,C,Stdin,Portability,我试着运行两个程序 更新之前 在第一个程序中,3个空字节和一个换行符可能不一定按该顺序写入屏幕;然后,假定命令行上没有I/O重定向,程序尝试从键盘读取数据。写入标准输入不会加载输入缓冲区。您通常可以写入标准输入,读取标准输出和标准错误,因为经典技术使用O_RDWR打开文件描述符,然后将其连接到标准I/O通道。但是,不能保证您可以这样做。顺便说一句,第一个项目需要 第二个程序有太多未定义的行为,很难分析。open调用需要三个参数,因为它包括O_CREAT;第三个参数是文件的模式,例如0644。您没

我试着运行两个程序

更新之前 在第一个程序中,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上失败,因为当您尝试读取时,文件指针位于文件的末尾。您可以通过此程序变体看到:

#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)