【问题标题】:Writing to stdin and reading from stdout (UNIX/LINUX/C Programming)写入标准输入并从标准输出读取(UNIX/LINUX/C 编程)
【发布时间】:2011-11-15 01:50:03
【问题描述】:

我正在处理一项任务,其中程序将文件描述符作为参数(通常来自 exec 调用中的父级)并从文件读取并写入文件描述符,在我的测试中,我意识到如果我使用 0、1 或 2 作为文件描述符,程序将从命令行运行并且不会给出错误。这对我来说很有意义,只是我可以写信给标准输入并让它显示在屏幕上。

对此有解释吗?我一直认为标准输入/标准输出有一些保护,你当然不能 fprintf 到标准输入或从标准输出 fgets。

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
    char message[20];
    read(STDOUT_FILENO, message, 20);
    write(STDIN_FILENO, message, 20);

    return 0;
}

【问题讨论】:

  • 请不要将文件 handles 与文件 descriptors 混淆。 您可能不被允许将 fprintfstdin(尽管我在标准中找不到任何明确禁止它的内容),但这对您是否可以 write 到文件描述符 0 的影响为零。
  • 到底有什么区别?他们没有完成相同的任务吗?
  • 胡萝卜和大棒可以完成相同的任务,但它们不是一回事,请问你友好的邻居驴子:-) 句柄是标准 C,描述符是 POSIX。

标签: c unix stdout stdin read-write


【解决方案1】:

尝试在标记为只读的文件上写入,反之亦然,这将导致 writeread 返回 -1 并失败。在这种特定 的情况下,stdin 和 stdout 实际上是同一个文件。本质上,在你的程序执行之前(如果你不做任何重定向),shell 会:

  if(!fork()){
       <close all fd's>
       int fd = open("/dev/tty1", O_RDWR);
       dup(fd);
       dup(fd);
       execvp("name", argv);
  }

因此,stdin、out 和 err 都是同一个文件描述符的副本,为读取和写入而打开。

【讨论】:

  • 更正,这不是由外壳而是由终端(模拟器)完成的。 shell 将像任何其他进程一样继承其 stdin/stdout 句柄。
【解决方案2】:
read(STDIN_FILENO, message, 20); 
write(STDOUT_FILENO, message, 20);

应该可以。注意 - 标准输出与标准输入不同(即使在命令行上)。您可以将来自另一个进程的输出作为标准输入提供给您的进程,或者将标准输入/标准输出安排为文件。

fprintf/fgets 有一个缓冲区 - 从而减少了系统调用的数量。

【讨论】:

    【解决方案3】:

    最佳猜测 - stdin 指向输入来自哪里,你的终端和 stdout 指向输出应该去哪里,你的终端。因为它们都指向同一个地方,所以它们可以互换(在这种情况下)?

    【讨论】:

    • 但他们可能不是你的终端。
    • @Ed 然后它将读取/写入 stdin/stdout 指向的任何内容。这个/那个很多。
    【解决方案4】:

    如果您在 UNIX 上运行程序

    myapp < input > output
    

    您可以打开 /proc/{pid}/fd/1 并从中读取,打开 /proc/{pid}/fd/0 并对其进行写入,例如,将 output 复制到 input。 (可能有更简单的方法可以做到这一点,但我知道它有效)

    只要你下定决心,你就可以做任何令人困惑的事情。 ;)

    【讨论】:

      【解决方案5】:

      很可能文件描述符 0、1 和 2 都对读写都是开放的(实际上它们都引用了相同的底层“打开文件描述”),在这种情况下你正在做什么将工作。但据我所知,没有保证,所以它也可能不起作用。我确实相信 POSIX 在某处指定如果在 shell 调用程序时将 stderr 连接到终端,它应该是可读和可写的,但我无法立即找到引用..

      一般来说,我建议不要从 stdout 或 stderr 读取,除非您正在寻找一个从中读取密码的终端,并且 stdin 已被重定向(不是 tty)。而且我建议永远不要写入标准输入 - 这很危险,您最终可能会破坏用户不希望写入的文件!

      【讨论】:

        【解决方案6】:

        如果要将消息写入stdin,可以打开当前tty,调用write系统调用将消息写入这个fd:

        string console_cmd = "hello";
        string tty = ttyname(STDIN_FILENO);
        int fd = open(tty.c_str(), O_WRONLY);
        write(fd, console_cmd.c_str(), console_cmd.size());
        

        同样,从标准输出读取。

        【讨论】:

          最近更新 更多