我试图运行两个程序。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
int n;
int k = 10;
int ret_val = 0;
ret_val = write (0, &k, sizeof(int));
if (-1 == ret_val)
{
printf ("Failed to write");
exit (EXIT_FAILURE);
}
scanf ("%d", &n);
printf ("Integer read is %d \n", n);
return 0;
}
然后我尝试了下一个。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int n;
int k = 10;
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_RDWR, S_IRWXU);
if (-1 == source_fd)
{
printf ("Failed to open file for reading");
exit (EXIT_FAILURE);
}
int stdin_fd;
/* Close STDIN_FILENO */
close(0);
/* dup the source */
stdin_fd = dup (source_fd);
if (-1 == stdin_fd)
{
printf ("Failed to dup");
exit (EXIT_FAILURE);
}
/* write to stdin_buffer (content will be taken from file_in.txt) */
ret_val = write (stdin_fd, &k, sizeof(int));
if (-1 == ret_val)
{
printf ("Failed to write to stdin_buffer");
exit (EXIT_FAILURE);
}
scanf ("%d", &n);
printf ("Integer read is %d \n", n);
close(source_fd);
return 0;
}
现在在第一种情况下,我无法写入标准输入。在第二种情况下,我能够从文件“file_in.txt”获取输入,并将内容发送到标准输入缓冲区。
我无法得到一个很好的解释,为什么我的第一个案例没有成功。谁能解释一下?
stdin
应该像任何其他文件一样吗?如果是写保护,那很好。但是当我重定向输入时(在第二种情况下),没有“权限被拒绝”的问题。这段代码似乎是不可移植的。是否有一种可移植且安全的方法来重定向文件中的stdin?
在完成评论后,我想出了一个更好的工作代码。我想对此代码提供一些反馈
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#define LEN 100
int main()
{
int n;
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)
{
perror ("Failed to open file for reading");
exit (EXIT_FAILURE);
}
/* Temp stdin_buffer */
int temp_fd = open ("temp_in.txt", O_CREAT | O_RDWR, S_IRWXU);
if (-1 == temp_fd)
{
perror ("Failed to open temp stdin");
exit (EXIT_FAILURE);
}
int stdin_fd;
/* Close STDIN_FILENO */
close(0);
/* dup the source */
stdin_fd = dup (temp_fd);
if (-1 == stdin_fd)
{
perror ("Failed to dup");
exit (EXIT_FAILURE);
}
ret_val = read (source_fd, buffer, LEN);
if (-1 == ret_val)
{
perror ("Failed to read from source");
exit (EXIT_FAILURE);
}
else
{
printf ("%s read from Source file\n", buffer);
}
/* write to stdin_buffer (content taken from file_in.txt) */
ret_val = write (stdin_fd, buffer, LEN);
if (-1 == ret_val)
{
perror ("Failed to write to stdin_buffer");
exit (EXIT_FAILURE);
}
ret_val = lseek (stdin_fd, 0, SEEK_SET);
if (-1 == ret_val)
{
perror ("Failed lseek");
exit (EXIT_FAILURE);
}
ret_val = scanf ("%d", &n);
if (-1 == ret_val)
{
perror ("Failed to read stdin_buffer");
exit (EXIT_FAILURE);
}
printf ("Integer read is %d \n", n);
close(source_fd);
return 0;
}
答案 0 :(得分:2)
在第一个程序中,3个空字节和一个换行符(可能)写入屏幕(不一定按顺序);程序然后尝试从键盘读取(假设命令行上没有I / O重定向)。写入标准输入不会加载输入缓冲区。您经常可以写入标准输入(并从标准输出和标准错误中读取),因为经典技术打开带有O_RDWR
的文件描述符,然后将其连接到标准I / O通道。但是,无法保证您可以这样做。 (顺便说一句,第一个程序需要<unistd.h>
。)
第二个程序有很多未定义的行为,很难分析。 open()
调用需要三个参数,因为它包含O_CREAT
;第三个参数是文件的模式(例如0644
)。你没有检查open()
是否成功。你没有检查写入是否成功;它不会,因为文件描述符被打开O_RDONLY
(或者,source_fd
被打开O_RDONLY
,而dup()
会将该模式复制到文件描述符0),这意味着write()
将失败。不检查输入操作(您不确保scanf()
成功)。 (第二个程序并不需要<sys/types.h>
或<sys/stat.h>
。)
基本上,由于您未检查任何关键功能调用,因此您不了解所发生的事情。
请注意,错误消息应写入标准错误,并应以换行符终止。
我得到了第一个按照规定工作的程序(Mac OS X 10.10 Yosemite,GCC 4.8.1),虽然很难证明空字节已写入标准输入(但是在那里写了换行符)。然后我可以键入10(或20,或100,或......)加上返回,然后打印该整数。
第二个程序在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;
}
它产生:
Failed to read from standard input: rc = -1
如果在阅读前回放文件,则会返回0;写入文件的二进制数据不是整数的有效字符串表示形式。
我编写了一个小函数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)
当scanf()
无法读取值时,它(通常)不会将任何内容写入相应的变量。这就是为什么99幸存下来的原因。如果您希望scanf()
可以读取整数数据,则需要:
#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)
答案 1 :(得分:0)
让我们看看你的三个节目在做什么......
计划1
第一个程序写入filedescriptor 0.默认情况下,这是stdout
,因此宏STDOUT_FILENO
具有该值。由于stdout
是一个运行到进程中的单向流,因此写入失败。
计划2
在此程序中,您打开一个文件,该文件可能获得FD(filedescriptor)3(标准流的0-2之后)。然后,你用FD 0关闭stdout。然后,你dup()
FD 3,因为第一个开放点位于索引0,那就是文件的新FD。由于scanf()只使用(未更改的)宏STDIN_FILENO,它将在那里获取该文件的内容。
计划3
在程序3中,您执行的操作与程序2完全相同,只是您打开文件两次。对上述情况的解释几乎遵循上述两点。
当你说'#34;更好的工作代码&#34;时,问题就是你的意思。关键是&#34;更好&#34;除非你指定你真正想要的东西,否则不可能说。根据您的主题和评论猜测,您希望远程控制第二个进程,就像在bash中使用管道符号一样。
为了做到这一点,你将不得不求助于特定于操作系统的方法,只需搜索&#34;输入输出重定向&#34;你应该能够找到一些信息。既然你已经提到了execl(),那么超出POSIX的可移植性(例如到win32)似乎不是一个问题,为此你应该找到很多示例代码,比我在这里写的要好得多。
无论如何,它归结为大致这些步骤:
pipe()
创建一对FD fork()
创建新流程dup2()
将输入FD从pipe()
重新分配给stdin 此外,您应关闭未使用的流或使用fcntl(FD_CLOEXEC)
对其进行配置,以便自动关闭它们。重要的一点是,两个FD保持连接到同一个(!)通道,尽管两个进程都有结束。关闭每一端的一端会在两个进程之间留下单向通道。