【问题标题】:C: Reading From Stdin Resets Terminal Attributes?C:从标准输入读取会重置终端属性?
【发布时间】:2015-06-09 12:15:00
【问题描述】:

我正在开发一个应该类似于 Unix 的“更多”命令的 C 项目。像“更多”一样,该程序应该能够从文件(如果指定)或标准输入(如果没有给出文件)获得输入(因此其他程序的输出可以通过管道输入)并将其显示到标准输出。也像“更多”一样,回显和规范模式应该被禁用,我已经使用以下代码完成了:

  //change terminal attributes
  tcgetattr(0, &info); //Get info
  tcgetattr(0, &orig_inf); //Save original info
  info.c_lflag &= ~ECHO; //Disable echo
  info.c_lflag &= ~ICANON; //Disable canonical mode
  info.c_cc[VMIN] = 1; //Get 1 char at a time
  tcsetattr(0, TCSANOW, &info); //Set attributes

为了从键盘读取用户命令,我明确地打开“dev/tty”,而不是仅仅从标准输入读取:

//Open cmd stream
if((cmd = fopen("/dev/tty", "r")) == NULL){
  perror("Failure opening command stream");
  tcsetattr(0, TCSANOW, &orig_inf);
  exit(EXIT_FAILURE);
}

并使用 getc(cmd) 读取它们。当用户提供要读取的文件时,这可以正常工作,但如果程序从标准输入接收输入,则终端属性似乎正在被重置。我可以看到我尝试输入的每个命令(这意味着回显再次打开),并且命令不会发送到程序,除非我按 Enter 键(意味着规范模式再次以某种方式重新激活)。我已经在网络和所有手册页中搜索了我正在使用的几乎所有系统调用,但似乎找不到原因。

如果有人知道为什么会发生这种情况以及如何解决它,我们将不胜感激。

谢谢!

【问题讨论】:

  • 您的评论说“更改终端属性”,但这不是您正在做的。您正在更改文件描述符零的属性,当您将标准输入重定向为从文件中读取时,它不是终端!
  • 您应该检查来自tcgetattr() 和来自tcsetattr() 的返回值。您没有说代码是通过来自文件 (yourprog < file) 还是通过管道 (someprog | yourprog) 的 I/O 重定向运行的,但无论哪种情况,对文件描述符 0 的操作都不会影响终端。很可能这两个函数都将errno 设置为ENOTTY(不是tty)。您需要打开/dev/tty 并控制其属性,而不是标准输入的属性。此外,atexit() 对于这样的程序非常有用。

标签: c unix


【解决方案1】:

似乎缺少的(至少问题中没有说明)是您正在打开 /dev/tty 以读取命令,并且正在通过文件描述符重置原始标准输入。但是没有提到使用fdreopendup2

  • 因为您指的是文件描述符0 (FILENO_STDIN),所以这与fileno(cmd) 不是同一个文件描述符——除非您使用dup2 来替换它。
  • 通常使用open 而不是fopen 打开设备,仅仅是因为open 提供了对进程的更多控制。然后使用fdreopen 从文件描述符中获取缓冲流。

例如,在/dev/tty 上使用fopen,您可能忽略了that 流中的实际设置,这些设置不一定与您在文件描述符0 上重新配置的设置相同.一个相关的问题可能是:What is the difference between stdin and STDIN_FILENO?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多