【问题标题】:Reading from STDIN from command line redirection only仅从命令行重定向读取 STDIN
【发布时间】:2022-01-20 00:30:47
【问题描述】:

我的目标是有一个程序可以从 C 中的文件重定向中获取用户输入。

$ ./hello < input.txt

但是当没有重定向或参数时,程序会输出一条消息:

$ ./hello
There is nothing to read!

由于文件重定向直接进入标准输入,我使用 scanf 来读取文件的内容。但是,当我在没有重定向的情况下启动程序时,scanf 等待输入,而我的目标是仅在没有任何输出消息的情况下从命令行上的重定向中获取它

【问题讨论】:

  • 你想对cat input.txt | ./hello做什么?您可以查看标准输入是否是终端——POSIX 中的isatty()。但这对管道数据没有帮助。将管道标识为输入并非易事。如果您指定./hello input.txt 并且如果没有要读取的参数则反对,这将是微不足道的,并且可以为用户节省一些输入。如果指定了多个名称,您还应该决定该怎么做——不要只忽略多余的名称;要么处理他们,要么抱怨他们的存在。
  • 为什么您需要这种对于标准命令行应用程序来说有些不寻常的行为?
  • 别再想“去”了。当您通过./hello &lt; input.txt 运行程序时,文件input.txt 不会“转到”标准输入。它标准输入。你可以fseek,倒带,做任何你喜欢的事情。也许您只是想使用isatty,或者您可以尝试寻找并查看stdin 是否可寻找,或者您可能想要完全不同的东西。

标签: c command-line


【解决方案1】:

在不可移植地检查 stdin 是否为 tty 之前,shell 无法判断输入的来源,它只能判断它是否已经存在。你没有说你在哪个平台上,我假设你真正想要的只是/为了不阻止读取/-你可以通过以非阻塞方式轮询 stdin 来实现这一点:

#include <sys/select.h>
#include <stdio.h>
#include <unistd.h>

int poll_stdin() {
struct timeval tv;
fd_set fds;

    tv.tv_sec = 0;
    tv.tv_usec = 0;
    FD_ZERO(&fds);
    FD_SET(STDIN_FILENO, &fds); 
    select(STDIN_FILENO+1, &fds, NULL, NULL, &tv);
    return FD_ISSET(STDIN_FILENO, &fds);
}

size_t read_stdin(char *buf, size_t max) {
size_t n = 0;

   while ((n < max) && (poll_stdin())) {
      read(STDIN_FILENO, &buf[n++], 1);
   }
   if (n < max-1) buf[n] = '\0';
   return n;
}

int main(int argc, char *argv[]) {
char buf[255];
size_t len;

   buf[255] = '\0';
   len = read_stdin(buf, 255);
   if (len == 0) printf("No input!\n");
   else printf(">%s\n", buf);
   return 0;
}

输出:

dtrombley@squall:~$ echo 'Hello, world!' > hello.txt
dtrombley@squall:~$ ./main
No input!
dtrombley@squall:~$ ./main < hello.txt
>Hello, world!

【讨论】:

  • 这可能对管道“奇怪”地工作,但在某些情况下你可能会发现它只接收部分输入......
  • 要解决这个问题,您还可以做一个只轮询一次然后执行 scanf 的版本。但是您应该知道,无论哪种情况,CLI 用户都会对您试图对他们进行事后猜测感到非常恼火。每个人都希望您的命令行实用程序阻止用户获取输入。这就是 CLI 的方式。如果你让它不这样做,你会以某种方式在某个地方破坏某人的脚本。
【解决方案2】:

使用 isatty()

这正是它的用途。

请注意,并非所有系统都提供健全的 isatty(),但任何声称符合 POSIX 的 *nixen 都可以正常工作。这是我当前的 *nixen 的首选代码(适用于 C):

#include <stdbool.h>
#include <unistd.h>

bool my_isatty( int id )
{
  return (0 <= id) and (id <= 2) and isatty( id );
}

Windows 上存在各种问题,特别是如果您使用 MSYS2 shell(它使用管道实现命令窗口,LOL,因此您必须使用一些低级操作系统的东西来检查)。

【讨论】:

    猜你喜欢
    • 2023-03-25
    • 1970-01-01
    • 1970-01-01
    • 2021-08-28
    • 2012-08-11
    • 2018-04-26
    • 2011-08-10
    • 1970-01-01
    • 2011-12-15
    相关资源
    最近更新 更多