char buf[1];
if (argc == 1) {
while (read(STDIN_FILENO, buf, 1) > 0) {
write(1, buf, sizeof(buf));
}
}
我想澄清一些有关此代码段的内容。我们运行它./exec_file假设我们只需按Enter键。我们移动到下一行并读取1个字节'\n'
然后将其写入stdout,使我们再下一行......简单到了。现在我们假设输入h
,然后输入。该程序会使用不可见的h
在下一行显示'\n'
。
在我们输入h
之后查看代码,它将其读入缓冲区然后将其写入stdout,但不知何故,程序等待在下一行吐出来,直到我按下Enter ...之后?
最后,当我们第一次点击while循环时,最初不会返回0
,因为我们最初没有输入任何东西?
答案 0 :(得分:3)
stdin
与大多数其他流的行为略有不同。
首先,输入是行缓冲的。这意味着在您按Enter键之前输入不可用。这解释了h
赢了,直到你按Enter键为止。
因为它是一个流,所以它并没有真正结束。当没有数据要读取时,呼叫将阻塞,直到某些数据可用(或直到程序收到信号)。套接字的工作方式相同。
使用fcntl
阻止行为can be turned off:
int fd = STDIN_FILENO;
int flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
答案 1 :(得分:1)
默认情况下,终端行缓冲,因为它处于规范模式。来自Linux手册tcgetattr(3)
:
规范和非规范模式
ICANON佳能的设定 c_lflag中的flag确定终端是否正在运行 规范模式(ICANON设置)或非规范模式(ICANON未设置)。 默认情况下,ICANON设置。
在规范模式中:
输入逐行可用。输入行是 可得到 当键入其中一个行分隔符时(NL,EOL,EOL2;或EOF at。) 行的开头)。除了EOF的情况,行分隔符 包含在read(2)返回的缓冲区中。
启用行编辑(ERASE,KILL;如果IEXTEN标志为 组: WERASE,REPRINT,LNEXT)。 read(2)最多返回一行 输入;如果read(2)请求的字节数少于可用的字节数 当前的输入行,然后只有所请求的字节数 阅读,剩下的字符将来可用 读(2)。
您可以通过使用正确的标记调用tcgetattr
来关闭终端上的规范模式。首先禁用规范模式;然后将超时设置为0;将最小读数设置为1以阻止读取,将0设置为非阻塞读取。通常习惯上也会禁用本地回声,否则您键入的所有内容仍然会自动显示(并在程序中显示两次):
#include <stdio.h>
#include <unistd.h>
#include <termios.h>
int main() {
struct termios old_settings, new_settings;
int is_terminal;
// check whether the stdin is a terminal to begin with
if (is_terminal = isatty(STDIN_FILENO)) {
// get the old settings
tcgetattr(STDIN_FILENO, &old_settings);
new_settings = old_settings;
// disable canonical mode and echo
new_settings.c_lflag &= (~ICANON & ~ECHO);
// at least one character must be written before read returns
new_settings.c_cc[VMIN] = 1;
// no timeout
new_settings.c_cc[VTIME] = 0;
tcsetattr(STDIN_FILENO, TCSANOW, &new_settings);
}
while (read(STDIN_FILENO, buf, 1) > 0) {
// add this here so that you can verify that it is character by character,
// and not the local echo from the terminal
write(STDOUT_FILENO, ">", 1);
write(STDOUT_FILENO, buf, sizeof(buf));
}
// finally restore the old settings if it was a terminal
if (is_terminal) {
tcsetattr(STDIN_FILENO, TCSANOW, &old_settings);
}
return 0;
}
答案 2 :(得分:0)
如果您仍希望阻止发生,但希望逐个字符地阅读,则可以使用termios
来配置如何将输入提供给您的程序。请参阅下面的代码。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
int main()
{
char buf[1];
struct termios term, term_orig;
if (tcgetattr(0, &term_orig)) {
printf("tcgetattr failed\n");
exit(-1);
}
term = term_orig;
term.c_lflag &= ~ICANON;
term.c_lflag |= ECHO;
term.c_cc[VMIN] = 1;
term.c_cc[VTIME] = 0;
if (tcsetattr(0, TCSANOW, &term)) {
printf("tcsetattr failed\n");
exit(-1);
}
while (read(0, buf, 1) > 0) {
write(1, buf, sizeof(buf));
}
return 0;
}