在看了《Linux/UNIX系统编程手册》和APUE前几章之后发现缺少相应的实践,如果只是单纯的了解API并没有太大意义,因此决定跟随《UNIX/Linux编程实践教程》将书中的例子调通与理解透。然后再去啃APUE,效果应该会好很多。
书中第一章给出了more实现的第一版,由于书写与2004年,代码看上去有些“另类”,修改后如下:
编译方法是直接使用gcc more01.c,然后./a.out more.01.c /etc/passwd
或从标准输入中获取信息直接输入./a.out
下面代码有几点需要注意:
if ( (fp = fopen(* ++argv, "r")) != NULL) //第一个参数是./a.out,跳过
```
关于main函数中argc argv参考[main函数arc argv介绍](https://www.52coder.net/post/main)
这行代码其实换成如下形式更好理解
``` C
while (argc--)
{
argv++;
if ( (fp = fopen(*argv, "r")) != NULL) //第一个参数是more01,跳过
{
do_more(fp);
fclose(fp);
}
else
exit(1);
}
再有就是关于 printf("\033[7m more? \033[m");中的形式之前没有见过,解释如下:
\033是8进制,它就是unix下终端转义符ESC(16进制1A,10进制27)
ESC[xm 是unix下改变终端输出颜色的命令
printf("\033[7m more?\033[m");
会显示一个白底黒字的more
输出特效格式控制:
```
\033[0m 关闭所有属性
\033[1m 设置高亮度
\03[4m 下划线
\033[5m 闪烁
\033[7m 反显
\033[8m 消隐
\033[30m -- \033[37m 设置前景色
\033[40m -- \033[47m 设置背景色
#### 版本一
``` C
/*more01.c
*read and print 24 lines then pause for a few special commands
*/
#include <stdio.h>
#include <stdlib.h>
#define PAGELEN 24
#define LINELEN 512
/*
*将数据显示在屏幕上,满一屏后,调用see_more函数接受用户的输入,以采取下一步动作。
*/
void do_more(FILE *);
int see_more();
int main(int argc, char * argv[])
{
FILE *fp;
//判断是文件还是标准输入,打开对应的数据源
if ( argc == 1)
do_more(stdin);
else
{
while (argc--)
if ( (fp = fopen(* ++argv, "r")) != NULL) //第一个参数是more01,跳过
{
do_more(fp);
fclose(fp);
}
else
exit(1);
}
return 0;
}
void do_more(FILE *fp)
{
char line[LINELEN];
int num_of_lines = 0;
int reply;
while ( fgets(line, LINELEN, fp) ) //逐行读
{
if (num_of_lines == PAGELEN) //满屏
{
reply = see_more();
if (reply == 0)
break;
num_of_lines -= reply; //根据reply重置行数
}
if ( fputs(line, stdout) == EOF )
exit(1);
num_of_lines++;
}
}
int see_more()
{
int c;
printf("\033[7m more? \033[m");
while ( (c = getchar()) != EOF ) //获取用户输入
{
if ( c == 'q' ) //退出
return 0;
if ( c == ' ' ) //下一页
return PAGELEN;
if ( c == '\n' ) //下一行
return 1;
}
return 0;
}
当然这个程序存在挺多问题的,比如输入q和空格后,需要按回车,而且输入是有回显,more会跟着屏幕滚动。
另外,一个比较严重的问题是,如果我们进行重定向:
gcc -o more01 more01.c
然后将生成的可执行文件放到以下任意一个目录就可以执行了:/bin /usr/bin /usr/local/bin.
ls /bin | more01
如果没有将可执行文件放在上面三个目录中任意一个的话只能用如下方式
ls /bin | ./more01
文件名后有*表示可执行文件。
期望的结果是将/bin目录下的文件分页,显示24行后暂停,实际结果并非如此,24行以后并没有暂停而是继续输出。
more01中的getchar()从标准输入读数据,ls /bin | ./more已经将more01的标准输入重定向到ls的标准输出。
解决的方法是从标准输入中读入要分页的数据,直接从键盘读用户的输入。
版本二
#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 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 ) ){
if ( num_of_lines == PAGELEN ) {
reply = see_more(fp_tty); /* NEW: pass FILE * */
if ( reply == 0 )
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' )
return 0;
if ( c == ' ' )
return PAGELEN;
if ( c == '\n' )
return 1;
}
return 0;
}
目前more命令与Linux中的more命令存在着两点主要区别:
1.输入回车换行之后,more跟着屏幕滚动
2.没有显示进度
随着学习的深入,这些问题将在后面解决。