【发布时间】: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()对于这样的程序非常有用。