0.目录

1.more 能做什么?

2.more 是如何实现的?

3.实现 more

1.more 能做什么?

more 可以分页显示文件的内容。正常运行后 more 会显示文件第一屏的内容,在屏幕的底部,more 用反白字体显示文件的百分比,这时如果按空格键,文件的下一屏内容会显示出来,如果按回车键,显示的则是下一行,如果输入“q”,结束显示,如果输入“h”,显示出来的是 more 的联机帮助。

more 有三种用法:

more filename

显示文件filename的内容。

command | more

more 将 command 命令的输出分页显示。

more < filename

从标准输入获取要分页显示的内容,而这时 more 的标准输入被重定向到文件 filename。

2.more 是如何实现的?

书中的流程图:

more 命令展示:
执行命令more test.c

按回车:

按空格:

按q:

3.实现 more

3.1 more01.c

/* more01.c - version 0.1 of more
 * read and print 24 lines then pause for a few special commands
 */

#include <stdio.h>
#include <stdlib.h>

#define PAGELEN 24
#define LINELEN 512

void do_more(FILE *);
int see_more();

int main( int ac, char *av[] )
{
    FILE *fp;

    if ( ac == 1 )
        do_more( stdin );
    else
        while ( --ac )
            if ( (fp = fopen( *++av, "r" )) != NULL )
            {
                do_more( fp );
                fclose( fp );
            }
            else
                exit(1);
    return 0;
}

void do_more( FILE *fp )
/*
 * read PAGELEN lines, then call see_more() for further instructions
 */
{
    char line[LINELEN];
    int num_of_lines = 0;
    int see_more(), reply;

    while ( fgets( line, LINELEN, fp ) ) {     /* more input */
        if ( num_of_lines == PAGELEN ) {       /* full screen? */
            reply = see_more();                /* y: ask user */
            if ( reply == 0 )                  /* n: done */
                break;
            num_of_lines -= reply;             /* reset count */
        }
        if ( fputs( line, stdout ) == EOF )    /* show line */
            exit(1);                           /* or die */
        num_of_lines++;                        /* count it */
    }
}

int see_more()
/*
 * print message, wait for response, return # of lines to advance
 * q means no, space means yes, CR means one line
 */
{
    int c;

    printf("\033[7m more? \033[m");            /* reverse on a vt100 */
    while( (c=getchar()) != EOF )              /* get response */
    {
        if ( c == 'q' )                        /* q -> N */
            return 0;
        if ( c == ' ' )                        /* ' ' => next page */
            return PAGELEN;                    /* how many to show */
        if ( c == '\n' )                       /* Enter key => 1 line */
            return 1;
    }
    return 0;
}

测试运行:

按两下回车:

按空格 + 回车:

按q + 回车:

代码分析:
这段代码有 3 个函数,在主函数中判断应该从文件还是标准输人中获取数据,并打开相应的数据源,然后调用 do_more 函数,do_more 将数据显示在显示器上,满一屏后,调用 see_more 函数接收用户的输入,以决定下一步的动作。

先来看对数据源的处理,在 main 函数中检查命令参数的个数,如果没有参数,那就从标准输入读取数据,这样一来 more 就可以通过管道重定向来得到数据,如:
who | more
who 命令列出当前系统中活动的用户,管道命令“|”将 who 的输出重定向到 more 的输入,结果是每次显示 24 个用户后暂停,在有很多用户的情况下,用 more 来对 who 的输出进行分页就会很有必要。
接下来是输入重定向的问题,看以下例子:
ls /bin | more01
期望的结果是将 /bin 目录下的文件分页,显示 24 行以后暂停。
然而实际的运行结果并不是这样的,24 行以后并没有暂停而是继续输出,问题在哪里呢?

已经将 more01 的标准输入重定向到 ls 的标准输出,这样 more01 将从同一个数据流中读用户的输入,这显然有问题。

程序缺陷:

  1. more01.c 只实现了查看一个文件( more filename ),当标准输入输出被重定向到其他管道时,程序无法正常接受来自键盘的信息(无法使用管道命令「|」、重定向「<」「>」)。
  2. 无法输入立即响应,需要按回车。

3.2 more02.c

如何改进?

  1. /dev/tty是键盘和显示器的设备描述文件,程序可以从/dev/tty得到键盘数据,避免因为重定向管道造成无法正常接收键盘数据。
  2. getchar() 相当于 getc(stdin)
/* more02.c - version 0.2 of more
 * read and print 24 lines then pause for a few special commands
 * feature of version 0.2: reads from /dev/tty for commands
 */
#include <stdio.h>
#include <stdlib.h>

#define PAGELEN 24
#define LINELEN 512

void do_more(FILE *);
int see_more(FILE *);

int main( int ac, char *av[] )
{
    FILE *fp;

    if ( ac == 1 )
        do_more( stdin );
    else
        while ( --ac )
            if ( (fp = fopen( *++av, "r" )) != NULL )
            {
                do_more( fp );
                fclose( fp );
            }
            else
                exit(1);
    return 0;
}

void do_more( FILE *fp )
/*
 * read PAGELEN lines, then call see_more() for further instructions
 */
{
    char line[LINELEN];
    int num_of_lines = 0;
    int see_more(FILE *), reply;
    FILE *fp_tty;

    fp_tty = fopen( "/dev/tty", "r" );          /* NEW: cmd stream */
    if ( fp_tty == NULL )                       /* if open fails */
        exit(1);                                /* no use in running */

    while ( fgets( line, LINELEN, fp ) ) {      /* more input */
        if ( num_of_lines == PAGELEN ) {        /* full screen? */
            reply = see_more( fp_tty );         /* NEW: pass FILE * */
            if ( reply == 0 )                   /* n: done */
                break;
            num_of_lines -= reply;              /* reset count */
        }
        if ( fputs( line, stdout ) == EOF )     /* show line */
            exit(1);                            /* or die */
        num_of_lines++;                         /* count it */
    }
}

int see_more(FILE *cmd)                         /* NEW: accepts arg */
/*
 * print message, wait for response, return # of lines to advance
 * q means no, space means yes, CR means one line
 */
{
    int c;

    printf("\033[7m more? \033[m");             /* reverse on a vt100 */
    while( (c=getc(cmd)) != EOF )               /* NEW: reads from tty */
    {
        if ( c == 'q' )                         /* q -> N */
            return 0;
        if ( c == ' ' )                         /* ' ' => next page */
            return PAGELEN;                     /* how many to show */
        if ( c == '\n' )                        /* Enter key => 1 line */
            return 1;
    }
    return 0;
}

测试运行:

3.3 more03.c

改进:不需要回车,直接立即响应输入的字符。

/* more03.c - version 0.3 of more
 * read and print 24 lines then pause for a few special commands
 * feature of version 0.3: no need to press return
 */
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>

#define PAGELEN 24
#define LINELEN 512

void do_more(FILE *);
int see_more(FILE *);
void set_crmode();
tty_mode(int how);

int main( int ac, char *av[] )
{
    FILE *fp;

    tty_mode(0);                                /* save tty mode */
    set_crmode();                               /* set chr-by-chr mode */

    if ( ac == 1 )
        do_more( stdin );
    else
        while ( --ac )
            if ( (fp = fopen( *++av, "r" )) != NULL )
            {
                do_more( fp );
                fclose( fp );
            }
            else
                exit(1);

    tty_mode(1);                                /* restore tty mode */
    return 0;
}

void do_more( FILE *fp )
/*
 * read PAGELEN lines, then call see_more() for further instructions
 */
{
    char line[LINELEN];
    int num_of_lines = 0;
    int see_more(FILE *), reply;
    FILE *fp_tty;

    fp_tty = fopen( "/dev/tty", "r" );          /* NEW: cmd stream */
    if ( fp_tty == NULL )                       /* if open fails */
        exit(1);                                /* no use in running */

    while ( fgets( line, LINELEN, fp ) ) {      /* more input */
        if ( num_of_lines == PAGELEN ) {        /* full screen? */
            reply = see_more( fp_tty );         /* NEW: pass FILE * */
            if ( reply == 0 )                   /* n: done */
                break;
            num_of_lines -= reply;              /* reset count */
        }
        if ( fputs( line, stdout ) == EOF )     /* show line */
            exit(1);                            /* or die */
        num_of_lines++;                         /* count it */
    }
}

int see_more(FILE *cmd)                         /* NEW: accepts arg */
/*
 * print message, wait for response, return # of lines to advance
 * q means no, space means yes, CR means one line
 */
{
    int c;

    printf("\033[7m more? \033[m");             /* reverse on a vt100 */
    while( (c=getc(cmd)) != EOF )               /* NEW: reads from tty */
    {
        if ( c == 'q' )                         /* q -> N */
            return 0;
        if ( c == ' ' )                         /* ' ' => next page */
            return PAGELEN;                     /* how many to show */
        if ( c == '\n' )                        /* Enter key => 1 line */
            return 1;
    }
    return 0;
}

void set_crmode()
/*
 * purpose: put file descriptor 0 (i.e. stdin) into chr-by-chr mode
 * method: use bits in termios
 */
{
    struct termios ttystate;

    tcgetattr( 0, &ttystate);                   /* read curr. setting */
    ttystate.c_lflag    &= ~ICANON;             /* no buffering */
    ttystate.c_cc[VMIN] = 1;                    /* get 1 char at a time */
    tcsetattr( 0, TCSANOW, &ttystate);          /* install settings */
}

/* how == 0 => save current mode; how == 1 => restore mode */
tty_mode(int how)
{
    static struct termios original_mode;
    if ( how == 0 )
        tcgetattr(0, &original_mode);
    else
        return tcsetattr(0, TCSANOW, &original_mode);
}

测试运行:

可以看到,输入 “q” 后直接退出,不需要按回车。

学习《Unix/Linux编程实践教程》(2):实现 more的更多相关文章

  1. 学习《Unix/Linux编程实践教程》(1):Unix 系统编程概述

    0.目录 1.概念 2.系统资源 3.学习方法 4.从用户的角度来理解 Unix 4.1 登录--运行程序--注销 4.2 目录操作 4.3 文件操作 5.从系统的角度来理解 Unix 5.1 网络桥 ...

  2. Unix/Linux编程实践教程(0:文件、终端、信号)

    本来只打算读这本书socket等相关内容,但书写得实在好,还是决定把其余的内容都读一下. 阅读联机帮助的一个示例: open系统调用: read系统调用: Unix的time: 上面的printf可以 ...

  3. Unix/Linux编程实践教程(二:socket、多线程、进程间通信)

    同一接口不同的数据源: 协同进程: fdopen以文件描述符为参数: fopen和popen: 为了实现popen,必须在子进程中调用sh,因为只有shell本身即/bin/sh可以运行任意shell ...

  4. Unix/Linux编程实践教程(一:进程、管道)

    execvp在程序中启动新程序: 用fork创建新进程: forkdemo2代码: 测试fork的时候参考<Linux权威指南>阅读笔记(3)  使用了patch: [root@local ...

  5. Unix/Linux编程实践教程(三:代码、测试)

    测试logfilec.c的时候,有个sendto(sock,msg,strlen(msg),0,&addr,addrlen),编译时提示: logfilec.c:30: warning: pa ...

  6. 我的学习经历——Linux系统入门教程

    我想把最近学习Linux的经验和过程分析出来,当时是在上大三,是学生一枚,以前对开源也没有什么特殊的认识,只觉得很高深,不明觉厉的东西,在当时因为学校要参加职业技能大赛,其中有一团体性质的比赛,几个同 ...

  7. Unix Linux 编程书籍

    UNIX环境高级编程(第3版) Advanced Programming in the UNIX Environment Linux/UNIX系统编程手册 Linux/UNIX系统编程手册 (豆瓣) ...

  8. Linux下more命令C语言实现实践 (Unix-Linux编程实践教程)

    1. more第一版 实现基础功能,显示每一页固定24行文本,“q Enter”退出, “Enter” 下一行, “space Enter”下一页. #include<stdio.h> # ...

  9. linux编程实践:实现pwd命令

    内核为每个目录都设置了一个指向自己的i节点入口,即".",还有一个指向其父目录i节点的入口,即"..",我们首先获取当前目录的i节点编号,但是并不能知道当前目录 ...

随机推荐

  1. 1-51单片机ESP8266学习-AT指令(开发板介绍)

    51单片机+ESP8266开发教程(AT指令篇) 开发板资源分布: 开发板部分原理图: 1--通信下载 2--51单片机 3--ESP8266(WIFI模块) 4--DHT11(温湿度传感器) 5-- ...

  2. 第4章 初识STM32

    第4章     初识STM32 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/firege ...

  3. iscsi target tgt架构

    tgt是用户态实现的iscsi target,而iet(iscsi enterprise target)是在内核态实现的target,tgt相比于iet来说,因为其用户态实现,方便调试,新加入一些功能 ...

  4. 20155318 《网络攻防》Exp3 免杀原理与实践

    20155318 <网络攻防>Exp3 免杀原理与实践 基础问题 杀软是如何检测出恶意代码的? 基于特征来检测:恶意代码中一般会有一段有较明显特征的代码也就是特征码,如果杀毒软件检测到有程 ...

  5. # 2017-2018-2 20155319『网络对抗技术』Exp4:恶意代码分析

    2017-2018-2 20155319『网络对抗技术』Exp4:恶意代码分析 实验目标与基础问题 ++1.实践目标++ 监控你自己系统的运行状态,看有没有可疑的程序在运行. 分析一个恶意软件,就分析 ...

  6. C++和python的变量对比

    <C++中的this和Python的self对比>基本都是针对函数而言的,从变量的角度看,也有相同之处. C++中,类中定义的变量一般叫做成员变量,或者说是成员属性,它只属于实例对象,只有 ...

  7. C++中前置声明介绍

    前置声明是指对类.函数.模板或者结构体进行声明,仅仅是声明,不包含相关具体的定义.在很多场合我们可以用前置声明来代替#include语句. 类的前置声明只是告诉编译器这是一个类型,但无法告知类型的大小 ...

  8. python删除文件与目录的方法

    python内置方法删除目录(空目录与非空目录)及文件 1.os.remove(file_path):删除文件 #PPTV是文件夹,xen.txt是文件 >>> os.remove( ...

  9. Jmeter(四)_16个逻辑控制器详解

    循环控制器: 指定其子节点运行的次数,可以使用具体的数值,也可以设置为变量 1:勾选永远:表示一直循环下去 2:如果同时设置了线程组的循环次数和循环控制器的循环次数,那循环控制器的子节点运行的次数为两 ...

  10. Jmeter+ant+Jenkins构建接口自动化测试

    1.已写好jmeter脚本 2.安装ant并将ant-jmeter-1.1.1.jar文件放入ant/lib目录,用于调用jmeter 3.修改jmeter的jmeter.properties文件(将 ...