曲径通幽论坛
标题:
初级 more 命令
[打印本页]
作者:
beyes
时间:
2008-11-16 23:15
标题:
初级 more 命令
#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);
//stdin 是标准输入,可以是来自键盘的输入,还可以来自被重定向的管道输入
else
while
( --ac )
/*若ac=2,则 -1 后,表示第2个参数,一般是要显示的文件名*/
if
( (fp = fopen(* ++av,
"r"
)) != NULL )
{
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;
//按要求显示内容
}
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
;
}
作者:
beyes
时间:
2008-11-16 23:15
标题:
深入解析 more01.c
上面的 more 命令雏形,存在一个问题,比如使用以下的命令:
ls /bin | ./more01.exe
则输出并不是在显示第 24 行后暂停下来,而是继续输出,那问题出在哪里了呢?
分析一下这个程序的执行流程:
在输入命令
ls /bin | ./more01.exe
时,因为 more01.exe 后不再带有参数,于是
ac=1
,那么执行:
do_more(stdin);
参数 stdin,是标准输入流,这个流的输入来源正好是来自 ls /bin 的输出然后通过管道 | 进入到 stdin 中。
接着,程序开始执行,按照流程,先是显示输入流中的 24 行的内容。显示完后,此时:
num_of_lines -= PAGELEN;
从而,程序进入到 see_more() 里。
上面的命令中,ls /bin 的输出是作为 标准输入 而进入到 more01.exe 中的;反过来就是说 more01.exe 的标准输入重定向到 ls 的标准输出。
这样,当 num_of_lines 一直增加到 24 行时,程序进入 see_more() 函数。此时需要注意的是,see_more()函数里,使用了getchar()从流中获得输入。getchar()函数是没有参数的,也就是说,只要是标准输入流不论是来自键盘或者来自文件流,getchar()都可以捕获---然而这个捕获,是不能指定的捕获--而使用 getc()函数却是可以指定流的,也就是说 getc()是带有参数的。在改进后的more 命令修改版本中,可以看到使用 getc() 的实现。
说回来,在 see_more() 函数里,getchar() 函数收到了什么呢?这里是很关键的。因为 fgets() 函数读取是以“指定的字节数-1”读入的,最后一个字节则以换行符
'\\n'
作为结束
。所以,see_more() 函数会返回 1。接下来回到 do_more()中后,程序就会连续的输出所有内容。
这里需要区分两种命令的不同效果:
./more01.exe text.txt
和
more text.txt | ./more01.exe
对于 more text.txt | ./more01.exe 中,上面已经分析过。这里再补充说明一下,more text.txt |是通过管道进行数据流的“强制灌输”。在这个数据流里,包含有潜藏的“回车键”输入。具体的说,就是当 getchar()每次在接收到换行符'\\n' 后,被管道强制给予了一个回车,所以我们看到程序是连续着没有暂停的输出。
然而,./more01.exetext.txt 这个又是另外一种不同的情况。和上面的命令的区别在于 ./more01.exe 后带不带参数。./more01.exetext.txt 是带有一个参数 text.txt 的。这样一来,程序是从 do_more(fp)进去的。其中的 fp 指的就是参数text.txt。此时,没有了像上面那种情况的管道强制性的灌输形为,所以,在显示完24行的内容后,需要手动的把 '\\n'送过去(即在键盘上按下回车键)。
再具体点的分析就是,当输出 24 行的内容后,文件指针执行了 '\\n' 这里,那么在使用 getchar() 后,文件指针现把 '\\n' 送给变量 c ,然后 fp++ 指向了下一个内容单元。
在第三帖中,给出修改后的 more01.exe 的修改版本 more02.exe,这个版本解决了连续输出的问题,但对于完善的 more 命令自然还是远远不够的。
作者:
beyes
时间:
2008-11-16 23:15
标题:
more02.c
#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 )
{
char
line[LINELEN];
int
num_of_lines
=
0
;
int
reply;
FILE
*
fp_tty;
fp_tty
=
fopen(
"/dev/tty"
,
"r"
);
if
( fp_tty
==
NULL
)
//假如读取设备描述文件失败则退出
exit(
1
);
while
( fgets( line, LINELEN, fp ) ){
if
( num_of_lines
==
PAGELEN ){
reply
=
see_more(fp_tty);
if
( reply
==
0
)
break
;
num_of_lines
-=
reply;
}
if
( fputs(line, stdout)
==
EOF )
exit(
1
);
num_of_lines
++
;
}
}
int
see_more
(
FILE
*
cmd)
{
int
c;
printf(
"
\\033
[7m more?
\\033
[m"
);
while
( (c
=
getc(cmd))
!=
EOF )
{
if
( c
==
'q'
)
return
0
;
if
( c
==
' '
)
return
PAGELEN;
if
( c
==
'\\n'
)
return
1
;
}
return
0
;
}
more02.c 解决了 more01.c 中关于连续输出的问题,这里的解决办法是:从标准输入中读入要分页的数据,直接从用户键盘独用户的输入。
文件 /dev/tty 是键盘和显示器的设备描述文件,向这个文件写相当于显示在用户的屏幕上,读相当于从键盘获取用户的输入。即使程序的输入/输出被 "<" 或 ">"重定向,程序还是可以通过这个文件与终端交换数据。
形象的说,在程序中使用了 /dev/tty 这个控制性行为后,似乎是给程序的输出上了一把锁,而这把锁的钥匙正是来自用户从键盘上的相应输入。
欢迎光临 曲径通幽论坛 (http://www.groad.net/bbs/)
Powered by Discuz! X3.2