`
javasee
  • 浏览: 961222 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

Linux终端(一)

阅读更多
在这章,我们将会考虑将我们第2章的程序进行改进。也许最明显的失败就是用户界面;他的功能也并不优雅。在这里,我们将会讨论如何更好的控制用户终端;也就说是键盘输入与屏幕输出。除了这些,我们还会了解我们编写的程序如何由用户处读取输入,即使是在输入重定向的情况下,以及确保输出到屏幕的正确位置。

尽管改进的CD数据程序会直到第7章我们才会看到,但是在这一章我们会做许多基础工作。第6章会关注于curses,这并不是某些远古的咒语,而提供一个代码高层来控制终端屏幕显示的函数库。同时,我们会通过介绍一些Linux和Unix的哲学思想以及终端输入输出的概念来检测一些早期Unix设置的想法。这里所提供的一些底层访问也许正是我们所寻找的。我们在这里所涉及的大部分内容也同样可以很好的适用于运行在终端窗口下的程序,例如KDE的Konsole,GNOME的gnome-terminal,或者是标准的X11 xterm。

在这一章,我们将会了解下面一些内容:

终端读取
终端驱动器以及通用终端接口
termios
终端输出与terminfo
检测按键

由终端读取与向终端写入

在第3章,我们了解到当一个程序由命令提示行启动时,shell会将标准的输入与输出流连接到我们的程序。我们可以通过使用getchar与printf例程来读取与写入这些默认流而与用户进行简单的交互。

下面我们要用C语言重写我们的菜单例程,只使用这两个例程,将其命名为menu1.c。

试验--C语言的菜单例程

1 由下面的代码行开始,其中定义要作为菜单使用的数组,以及getchoice函数原型:

#include <stdio.h>
char *menu[] = {
“a - add new record”,
“d - delete record”,
“q - quit”,
NULL,
};
int getchoice(char *greet, char *choices[]);

2 main函数使用例子菜单menu调用getchoice:

int main()
{
int choice = 0;
do
{
choice = getchoice(“Please select an action”, menu);
printf(“You have chosen: %c\n”, choice);
} while(choice != ‘q’);
exit(0);
}

3 下面是重要的代码:打印菜单与读取用户输入的函数:

int getchoice(char *greet, char *choices[])
{
int chosen = 0;
int selected;
char **option;
do {
printf(“Choice: %s\n”,greet);
option = choices;
while(*option) {
printf(“%s\n”,*option);
option++;
}
selected = getchar();
option = choices;
while(*option) {
if(selected == *option[0]) {
chosen = 1;
break;
}
option++;
}
if(!chosen) {
printf(“Incorrect choice, select again\n”);
}
} while(!chosen);
return selected;
}

工作原理


getchoice函数打印程序简介greet与以及例子菜单choices,然后要求用户选择一个初始字符。程序会循环直到getchar函数返回一个与option数组实体的第一个字符匹配的字符。

当我们编译运行这个程序时,我们地发现他并不如我们期望的那样运行。下面是一此终端会活来演示这个程序:

$ ./menu1
Choice: Please select an action
a - add new record
d - delete record
q - quit
a
You have chosen: a
Choice: Please select an action
a - add new record
d - delete record
q - quit
Incorrect choice, select again
Choice: Please select an action
a - add new record
d - delete record
q - quit
q
You have chosen: q
$

在这里用户必须输入A/Enter/Q/Enter来做出选择。这至少有两个问题:最严重的问题是我们在每次正确的选择之后都会得到Incorrect choise输出;另外,我们必须在程序读取我们的输入之前按下Enter。

典型与非典型模式

这两个问题是紧密相关的。在默认情况下,终端输入直到用户按下Enter下才会为程序可用。在大多数情况下,这是一个好处,因为他可以允许用户使用Backspace或是Delete修正输入错误。只有当他们对他们在屏幕上看到的感到满意以后才会按下Enter使得输入为程序可用。

这种行为称之为典型模式,或是者是标准模式。所有的输入都是以行的方式进行处理的。在一行输入完整(通常是当用户按下Enter时),终端界面管理所有的按键输入,包括Backspace,而且程序不会读取任何字符。

这种模式的相反面为非典型模式,此时程序在输入字符的处理上有更多的控制权。我们会在后面再回来讨论这两种模式。

除了这些之外,Linux终端处理器喜欢将字符转换为信号,并且可以为我们自动执行Backspace与Delete,所以在我们所编写的程序中并不需要重新实现。我们将会在第11章讨论更多关于信号的内容。

那么在我们的程序中发生了什么呢?Linux在用户按下Enter之前会保存输入,然后将选择的字符与后面的Enter发送给程序。所以每次我们输入一个菜单选项时,程序调用getchar,处理字符,然后再次调用getchar,此时会立即返回Enter字符。

程序实际看到的字符并不是一个ASCII码的回车,CR(十进制13,十六进制0D),而换行(十进制10,十六进制0A)。这是因为Linux(类似Unix)内部总是使用换行来结束文本行;也就是说,Unix只使用一个换行来表示新行,而其他的系统,例如MS-DOS,使用回车和换行来表示新行。如果输入或是输出设备也发送或是请求一个回车,Linux终端会小心的进行处理。如果我们习惯于使用MS-DOS或是其他的环境,那就会显得有一些奇怪,但是这样考虑的一个好处是Linux中文本与二进制之间并没有真正的区别。只有当我们向一个终端或是打印机,绘图仪时输入或输出时才会处理回车。

我们可以使用一些代码来忽略额外的换行符来简单的修改我们的菜单例程的主要缺陷,如下所示:

do {
selected = getchar();
} while(selected == ‘\n’);

这解决了第一个问题。我们回到需要按下回车的第二个问题,而我们会在后面讨论一个更好的处理换行的方法。

处理重定向输出

对于Linux程序,可以很容易的将他们的输入或输出重定向到一个文件或是其他的程序。让我们看一下当我们将输出重定到一个文件时我们的程序是如何处理的:

$ menu1 > file
a
q
$

我们可以认为这是成功的,因为车出重定向到一个文件而不是终端。然而,这里却有我们希望阻止的情况,或者说我们希望分离提示,我们希望用户可以从其他的输出查看,从而可以安全的重定向。

我们可以通过检测是一个底层文件描述符是否与一个终端相关联来区别标准输出是否已被重定向。isatty系统调用可以完成这个工作。我们只需要简单的传递给他们一个可用的文件描述符,他就可以检测出这个文件描述符是否连接到一个终端。

#include <unistd.h>
int isatty(int fd);

如果打开的文件描述符fd连接到一个终端,isatty系统调用就会返回1,否则返回0。

在我们的程序中,我们使用文件流,但是isatty只可以操作一个文件描述符。为了提供必要的转换,我们需要结合使用isatty调用与我们在第3章讨论的fileno例程。

如果stdout已经被重定向了我们要怎么办呢?仅是退出是不够的,因为用户并不知道程序为什么会运行失败。在stdout上打印一条信息也没有用,因为他已经被重定向离开终端了。一个解决办法就是写入stderr,此时他并没有被shell命令>file进行重定向。

试验--检测输出重定向

使用我们在前面所编写的程序menu1.c,包含一个新的include,将main改为下面的代码,并将其称为menu2.c.

#include <unistd.h>
...
int main()
{
int choice = 0;
if(!isatty(fileno(stdout))) {
fprintf(stderr,”You are not a terminal!\n”);
exit(1);
}
do {
choice = getchoice(“Please select an action”, menu);
printf(“You have chosen: %c\n”, choice);
} while(choice != ‘q’);
exit(0);
}

工作原理

新版本的代码使用isatty函数来测试标准是否连接到一个终端,如果不是则会结束执行。同样也可以使用shell测试来决定是束提供一个提示符。当然可以,而且也比较常见的是同时重定向stdout与stderr,从而使其离开终端。我们可以像如下的样子将错误流重定向到一个不同的文件:

$ menu2 >file 2>file.error
$

或者是将两个输出流组合到一个文件中,如下所示:

$ menu2 >file 2>&1
$

在这个例子中,我们需要向控制台发送一条消息。

与终端交互

如果我们需要阻止我们程序中与用户交互的部分被重定向,但是对于其他的输入或是输出我们还是允许发生的,此时我们需要分离与stdout和stderr的交互。我们可以通过直接读写终端来做到。因为Linux是一个多用户系统,通常有许多终端直接相连或是通过网络相连,那么我们如何来确定要使用的正确终端呢?

幸运的,Linux和Unix系统通过提供一个特殊的设备,/dev/tty,从而使得事情变得简单,这个设备通常是当前的终端或是登陆会话。因为Linux将所有的内容都看作文件,我们可以使用通常的文件操作来读写/dev/tty设备。

现在我们要来修改我们的选择程序,从而我们可以向getchoice例程传递参数,来更好的控制输出。我们将命名为menu3.c。

试验--使用/dev/tty

打开menu2.c,将其内容改为下列的样子,这样输入和输出就可以重定向到/dev/tty:

#include <stdio.h>
#include <unistd.h>
char *menu[] = {
“a - add new record”,
“d - delete record”,
“q - quit”,
NULL,
};
int getchoice(char *greet, char *choices[], FILE *in, FILE *out);
int main()
{
int choice = 0;
FILE *input;
FILE *output;
if(!isatty(fileno(stdout))) {
fprintf(stderr,”You are not a terminal, OK.\n”);
}
input = fopen(“/dev/tty”, “r”);
output = fopen(“/dev/tty”, “w”);
if(!input || !output) {
fprintf(stderr,”Unable to open /dev/tty\n”);
exit(1);
}
do {
choice = getchoice(“Please select an action”, menu, input, output);
printf(“You have chosen: %c\n”, choice);
} while(choice != ‘q’);
exit(0);
}
int getchoice(char *greet, char *choices[], FILE *in, FILE *out)
{
int chosen = 0;
int selected;
char **option;
do {
fprintf(out,”Choice: %s\n”,greet);
option = choices;
while(*option) {
fprintf(out,”%s\n”,*option);
option++;
}
do {
selected = fgetc(in);
} while(selected == ‘\n’);
option = choices;
while(*option) {
if(selected == *option[0]) {
chosen = 1;
break;
}
option++;
}
if(!chosen) {
fprintf(out,”Incorrect choice, select again\n”);
}
} while(!chosen);
return selected;
}

现在,当我们使用输出重定向来运行这个程序时,我们可以看到提示符与通常的程序输出是分离的:

$ menu3 > file
You are not a terminal, OK.
Choice: Please select an action
a - add new record
d - delete record
q - quit
d
Choice: Please select an action
a - add new record
d - delete record
q - quit
q
$ cat file
You have chosen: d
You have chosen: q

分享到:
评论

相关推荐

    实验一_登录Linux终端.docx

    Linux 终端登录实验是 Unix 操作系统的基础实验之一,本实验旨在让学生掌握 Linux 终端的基本操作,包括登录系统、使用 shell、创建新用户、切换虚拟终端等。 一、实验准备 在进行 Linux 终端登录实验之前,需要...

    linux 终端

    linux 终端 linux 终端 操作工具

    基于ARM的嵌入式Linux终端系统性能实时优化.pdf

    本文提出了一种基于ARM的嵌入式Linux终端系统性能实时优化方法,该方法通过对ARM硬件的中断管理和数据处理,实现了Linux终端系统实时性优化。 首先,本文讨论了基于ARM的嵌入式Linux终端系统内存优化问题。通过对...

    Linux终端的设计与应用

    基于这一背景,石家庄职业技术学院的研究人员提出了一种以Linux为核心的设计方案——Linux终端,该系统不仅具有高性能和低廉的价格优势,而且易于二次开发和维护。 #### 关键词解析 - **ARM指令集**:ARM架构是一...

    WinSCP linux 终端工具

    总的来说,WinSCP作为一款强大的Linux终端工具,不仅提供了便捷的文件管理功能,还通过其直观的界面和丰富的特性,极大地提高了用户在Linux环境下的工作效率。无论是初学者还是经验丰富的系统管理员,都能从中受益。

    Linux终端实用技巧篇.docx

    Linux 终端实用技巧篇 本篇文章将详细介绍 Linux 终端中的实用技巧和命令,旨在帮助用户更好地掌握 Linux 终端的使用。以下是文章中的知识点总结: 一、基本命令 * Tab 键组合:在终端输入命令时,可以使用 Tab ...

    linux 终端快捷键.pdf

    Linux终端快捷键是提高操作效率的重要工具,尤其对于系统管理员和需要频繁使用命令行的用户来说,掌握它们可以显著提升工作效率和操作精准度。Linux系统自诞生以来,其命令行界面一直以其强大的灵活性和控制能力被...

    Linux终端下的直接写屏技术

    Linux终端下的直接写屏技术Linux终端下的直接写屏技术Linux终端下的直接写屏技术

    Linux终端仿真软件SecureCRT-5.13.rar

    SecureCRT作为一款专业的Linux终端仿真软件,凭借其丰富的功能和良好的用户界面,深受IT专业人士喜爱。无论你是初学者还是经验丰富的系统管理员,掌握SecureCRT都能极大地提高你在Linux环境中的工作效率。通过不断...

    Teplayer linux终端播放器

    解码器用的是神器ffmpeg,所以在终端下可以播放大部分视频,图片,动态图片。使用时改变终端的字体大小,行数,列数,最大行数,最大列数可调节观看效果。播放器有两种播放模式,默认灰度播放,输入1使能颜色播放。...

    linux 终端和颜色控制说明

    很实用的linux 终端和颜色控制说明,觉得还不错,拿出来分享一下。

    宁盾多因子认证(MFA)与Linux终端安全认证方案.pdf

    宁盾多因子认证(MFA)与Linux终端安全认证方案.pdf宁盾多因子认证(MFA)与Linux终端安全认证方案.pdf宁盾多因子认证(MFA)与Linux终端安全认证方案.pdf宁盾多因子认证(MFA)与Linux终端安全认证方案.pdf宁盾多因子认证...

    linux-超级终端

    Linux超级终端,也被称为控制台或TTY(Teletype),是Linux系统中一个重要的命令行界面,用于远程或本地访问操作系统。它为用户提供了一个与操作系统交互的文本接口,允许用户执行各种系统管理任务,如运行命令、...

    linux终端常用命令总结

    linux终端常用命令总结,有需要或者忘记的可以看一下。

    linux终端常用的快捷键

    这是一个txt的文本文件,主要总结了一些常见linux终端的快捷的使用。

    linux终端窗口下的打字练习软件

    可以在linux下的终端窗口或者虚拟终端里使用的打字练习软件。这个软件是用C语言编写的。这是一个压缩文件,需要先解压缩。接着进行编译,输入如下命令:gcc -o easy-type easy-type.c ,然后输入命令: ./easy-type ...

    C#通过ssh调用linux终端及文件上传

    提供的"linux终端调用demo及上传文件"可能包含了一个示例项目,演示了如何在C#中实际应用上述方法。这个项目可能会包含C#代码文件,用于建立SSH连接,执行命令,以及使用Plink或SCP进行文件上传。通过学习和分析这些...

    Linux系统字符终端界面的编程(1)——CURSES库简介.pdf

    CURSES 库是一个基于 Linux 操作系统的字符终端界面编程库,它提供了一个完整的函数集合,能够帮助开发者快速开发出高质量的字符终端界面程序。 CURSES 库的主要功能包括: 1. 窗口管理:CURSES 库提供了窗口管理...

    向Linux终端服务器迁移.pdf

    这个方案的安装和配置过程简单快捷,大约只需要一个小时,使得任何企业都能轻松体验到Linux终端服务器的强大功能。 Linux终端服务器项目组的努力使得迁移过程变得容易。使用Linux终端服务器,企业可以显著降低系统...

Global site tag (gtag.js) - Google Analytics