linux终端一直输出怎么停止,linux终端一直在刷屏
在本章中,我们将考虑改进第2章中的过程。也许最明显的失败是用户界面;他的功能也不优雅。在这里,我们将讨论如何更好地控制用户终端;也就是说键盘输入和屏幕输出。除了这些,我们还将学习我们编写的程序如何读取用户的输入,即使输入被重定向,并确保输出在屏幕上的正确位置。
虽然改进后的CD数据程序要到第7章才能看到,但是这一章我们会做很多基础工作。第6章将重点介绍诅咒,这不是一些古老的咒语,而是提供了一个高级代码库来控制终端屏幕的显示。同时我们会通过介绍Linux和Unix的一些哲学思想以及终端输入输出的概念来检验早期Unix设置的一些思路。这里提供的一些底层访问可能正是我们正在寻找的。我们在这里讨论的大部分内容也可以很好地应用于终端窗口下运行的程序,比如KDE的Konsole,gnome的gnome-terminal,或者标准的X11 xterm。
在本章中,我们将学习以下内容:
终端读数
终端和通用终端接口
结构
输出和终端信息
检测键
从终端读取和写入终端
在第三章中,我们学习了当一个程序通过命令行启动时,shell会将标准的输入和输出流连接到我们的程序。我们可以通过使用getchar和printf例程来读写这些默认流,从而简单地与用户进行交互。
接下来,我们将用C语言重写我们的菜单例程,只使用这两个例程,并将它们命名为menu1.c
C语言实验菜单程序
1从下面一行代码开始,它定义了用作菜单的数组和getchoice函数的原型:
#包含stdio.h
char *menu[]={
" a -添加新记录"、
"删除记录",
“q戒”,
空,
};
int getchoice(char *greet,char * choices[]);
主函数使用示例菜单menu调用getchoice:
int main()
{
int choice=0;
做
{
choice=getchoice("请选择一个动作",菜单);
printf("您已选择:%c/n ",choice);
} while(选择!= q );
退出(0);
}
3以下是重要代码:打印菜单和读取用户输入的功能:
int getchoice(char *greet,char *choices[])
{
int choosed=0;
int选择;
char * *选项;
做{
printf("选择:%s/n ",greet);
选项=选择;
while(*选项){
printf("%s/n ",*选项);
选项;
}
selected=getchar();
选项=选择;
while(*选项){
if(selected==*option[0]) {
choosed=1;
打破;
}
选项;
}
如果(!已选择){
printf("选择不正确,重新选择/n ");
}
} while(!被选中);
返回所选内容;
}
操作原理
getchoice函数打印程序介绍问候和示例菜单选项,然后要求用户选择一个初始字符。程序循环,直到getchar函数返回一个与选项数组实体的第一个字符匹配的字符。
当我们编译并运行这个程序时,我们发现它并没有像我们预期的那样运行。以下是该终端对该程序的演示:
$ ./菜单1
选择:请选择一项活动
a -添加新记录
删除记录
q -退出
a
您选择了:a
选择:请选择一项活动
a -添加新记录
删除记录
q -退出
选择不正确,请重新选择
选择:请选择一项活动
a -添加新记录
删除记录
q -退出
q
你选择了:问
$
这里,用户必须输入A/Enter/Q/Enter来做出选择。至少有两个问题:最严重的问题是,在每一个正确的选择之后,我们会得到不正确的选择输出;此外,在程序读取我们的输入之前,我们必须按Enter键。
典型和非典型模式
这两个问题密切相关。默认情况下,在用户按Enter键之前,程序无法使用终端输入。在大多数情况下,这是一个优势,因为它允许用户使用退格或删除来纠正输入错误。只有当他们对他们在屏幕上看到的感到满意时,他们才会按Enter键,使输入对程序可用。
这种行为被称为典型模式,或者它是标准模式。的所有输入都按行处理。一行中的输入完成(通常在用户按Enter时)。终端界面管理所有的按键输入,包括退格键,程序不会读取任何字符。
与这种模式相反的是非典型模式,在这种模式下,程序对输入字符的处理有更多的控制权。后面我们再回来讨论这两种模式。
另外,Linux终端处理器喜欢把字符转换成信号,可以自动替我们执行退格和删除,所以不需要在我们写的程序中重新实现。我们将在第11章详细讨论信号。
那么在我们的程序中发生了什么呢?Linux在用户按回车键之前保存输入,然后将选中的字符和后面的回车键发送给程序。所以每次我们输入一个菜单选项,程序调用getchar,处理字符,然后再次调用getchar,会立即返回Enter字符。
程序实际看到的字符不是ASCII码的回车,CR(十进制13,十六进制0D),而是换行符(十进制10,十六进制0A)。这是因为Linux(类似于Unix)总是使用换行符来结束文本行;也就是说,Unix只使用换行符来表示新的一行,而其他系统,比如MS-DOS,则使用回车和换行符来表示新的一行。如果输入或输出设备也发送或请求回车,Linux终端会小心处理。如果我们习惯使用MS-DOS或其他环境,会显得很奇怪,但这种考虑的一个好处是,在Linux中文本和二进制并没有真正的区别。只有当我们输入或输出到终端或打印机或绘图仪时,我们才会处理回车。
我们可以使用一些代码,通过忽略额外的换行符来简单地修改菜单例程的主要缺陷,如下所示:
做{
selected=getchar();
} while(selected==/n );
这解决了第一个问题。让我们回到按回车键的第二个问题,稍后我们会讨论一个更好的处理换行符的方法。
处理重定向输出
对于Linux程序,很容易将它们的输入或输出重定向到一个文件或其他程序。让我们看看当我们将输出重置为文件时,我们的程序是如何处理的:
$ menu1文件
a
q
$
我们可以认为这是成功的,因为汽车被重定向到一个文件而不是一个终端。但是,有些情况是我们希望阻止的,或者我们希望将提示分开,我们希望用户从其他输出中查看它们,以便安全地重定向它们。
我们可以通过检测底层文件描述符是否与终端相关联来区分标准输出是否已经被重定向。Isatty系统调用可以做到这一点。我们只需要简单地给他们传递一个可用的文件描述符,他就可以检测出这个文件描述符是否连接到了终端。
#包括unistd.h
int is atty(int FD);
如果打开文件描述符fd连接到一个终端,isatty系统调用将返回1,否则将返回0。
在我们的程序中,我们使用文件流,但是isatty只能操作一个文件描述符。为了提供必要的转换,我们需要使用isatty来调用我们在第3章中讨论过的fileno例程。
如果stdout被重定向了,我们该怎么办?仅仅退出是不够的,因为用户不知道程序失败的原因。在stdout上打印消息是没有用的,因为他已经被重定向离开了终端。一种解决方案是写入stderr,它不会被shell命令文件重定向。
测试-检测输出重定向
使用我们之前编写的程序menu1.c,包括一个新的include,将main更改为以下代码,并将其命名为menu2.c。
#包括unistd.h
.
int main()
{
int choice=0;
如果(!isatty(fileno(stdout))) {
fprintf(stderr,"你不是终端!/n ");
出口(1);
}
做{
choice=getchoice("请选择一个动作",菜单);
printf("您已选择:%c/n ",choice);
} while(选择!= q );
退出(0);
}
操作原理
新版本的代码使用isatty函数来测试标准是否连接到终端,如果没有,它将结束执行。您还可以使用shell测试来确定包是否提供提示。当然,同时重定向stdout和stderr是很常见的,这样它们就离开了终端。我们可以将错误流重定向到不同的文件,如下所示:
$ menu2 file 2 file.error
$
或者将两个输出流合并成一个文件,如下所示:
$ menu2文件2 1
$
在本例中,我们需要向控制台发送一条消息。
与终端交互
如果我们需要防止程序中与用户交互的部分被重定向,但我们仍然允许其他输入或输出发生,那么我们需要将与stdout和stderr的交互分开。我们可以通过直接读写终端来实现。因为Linux是多用户系统,通常会有很多终端直接连接或者通过网络连接,那么我们如何确定使用正确的终端呢?
幸运的是,Linux和Unix系统通过提供一个特殊的设备/dev/tty使事情变得简单,这个设备通常是当前的终端或登录会话。因为Linux把一切都当作文件,所以我们可以使用通常的文件操作来读写/dev/tty设备。
现在我们将修改我们的选择程序,以便我们可以将参数传递给getchoice例程,从而更好地控制输出。我们将把它命名为menu3.c
实验-使用/开发/tty
打开menu2.c并将其内容更改为以下内容,以便输入和输出可以重定向到/dev/tty:
#包含stdio.h
#包括unistd.h
char *menu[]={
" a -添加新记录"、
"删除记录",
“q戒”,
空,
};
int getchoice(char *greet,char *choices[],FILE *in,FILE * out);
int main()
{
int choice=0;
文件*输入;
文件*输出;
如果(!isatty(fileno(stdout))) {
fprintf(stderr,“你不是终端,好吧。/n ");
}
input=fopen("/dev/tty "," r ");
output=fopen("/dev/tty "," w ");
如果(!输入!输出){
fprintf(stderr,“无法打开/dev/tty/n”);
出口(1);
}
做{
choice=getchoice("请选择一个动作",菜单,输入,输出);
printf("您已选择:%c/n ",choice);
} while(选择!= q );
退出(0);
}
int getchoice(char *greet,char *choices[],FILE *in,FILE *out)
{
int choosed=0;
int选择;
char * *选项;
做{
fprintf(out," Choice: %s/n ",greet);
选项=选择;
while(*选项){
fprintf(out,“%s/n”,* option);
选项;
}
做{
selected=fgetc(in);
} while(selected==/n );
选项=选择;
while(*选项){
if(selected==*option[0]) {
choosed=1;
打破;
}
选项;
}
如果(!已选择){
fprintf(out,“选择不正确,重新选择/n”);
}
} while(!被选中);
返回所选内容;
}
现在,当我们使用输出重定向来运行这个程序时,我们可以看到提示符与通常的程序输出是分开的:
$ menu3文件
你不是终端,好吧。
选择:请选择一项活动
a -添加新记录
删除记录
q -退出
d
选择:请选择一项活动
a -添加新记录
删除记录
q -退出
q
$ cat文件
您选择了:d
你选择了:问