UNIX命令行管道机制-UNIX哲学

简介:

UNIX从来都不是为人机交互而设计的,而是为程序之间的交互而设计的。

用了多年Linux,从起初的羡慕,崇拜,到初学时的不解,混乱,愤怒,后来失望,困惑,...最终发现,如果你真的非要和Windows相比较的话,UNIX的人机交互确实表现不佳,正是这种不佳才导致了在使用过程中的种种问题,比如愤怒,比如失望...但是当我真正理解了UNIX的设计初衷的时候,这才彻底明白了一些事情的真相。正如Windows拥有那么多的UI元素以及纷繁惹人眼的绚丽控件以获得使用者最大的舒适度一样,UNIX设计了shell管道以获得程序之间交互的最大舒适度。
        UNIX是以小为美的设计典型,和Windows不属于一个理念。但是如果你把UNIX的一个命令理解成Windows界面的一个控件的话,或许会好很多,人们会操作(点击,下拉,拖移...)一个控件以获得一种效果,而UNIX程序也会将输出重新输入给另一个程序以获取一种效果,和Windows不同的是,这个操作一般不需要人的参与,全是程序之间的事情,人的作用往往体现在程序的组织上,程序之间如何来组织,如何来交互,这需要人来告诉UNIX系统-实际上也就是编写一个脚本。
        因此,越小的程序越好,便于人们去组织它们,这样它们的(使用代码行/总体代码行)这个比值是最高的,也正是因此,这个事实浓缩成了UNIX的另一个哲学:小程序只做好一件事。其实,我们发现,这条哲学可以从UNIX终极哲学中衍生出来。程序间的交互远远没有人机交互复杂,我们只需要想一下程序要如何表达自己的功能就可以了,归结于一点,那就是程序必须要有产出,也就是输出,而输入就是其原材料,程序扮演了加工者的角色,也就是一个过滤器,好的程序始终不要自己产生输出,正如没有能源输入,永动机早晚要停一样,程序的输出必须由输入加工而来,而最终的数据来自于人。因此下面的哲学被衍生了出来:
1.程序只是一个过滤器
2.程序之间的交互就是输入和输出

因此管道的概念就被呼出来了!什么是管道?管道就是一个pipe(??!fk!),将一个程序的输出和另一个程序的输入联系起来的一根管子。UNIX的命令行管道如下图所示


将各个程序画成阶梯状完全是为了展示管道,实际上它们都是同时执行的,只是启动先后顺序不同而已,一个命令行可以通过“|”来分隔为多个命令,前面命令的输出作为紧接着后面命令的输入,通过管道彼此相连接。

        这种管道到底是怎么实现的呢?如果看一下shell的源代码,比如csh,bash等,那固然不错,可是却容易被太多的额外处理混淆了视听,你很难从复杂的shell源代码中抽出哪些是和命令行管道相关的,加上代码本身的调用层次很深,看懂代码就更佳困难,除非你专门想研究一下shell的实现,否则如果仅仅想知道命令行管道这么一件简单的事情的话,还是自己实现一个为好。这难道不矛盾吗?你都不知道怎么实现的,怎么自己实现啊?!可是你知道效果啊,你也知道规范,这些就够了,这里又有一个不成文的规则: 当你只知道效果和规范的时候,自己动手实现一个符合规范的机制之后,你就会明白该机制是怎么实现的了。
        因此,代码不重要,重要的是设计本身!而设计是属于比较高层面的概念,其下是复杂的逻辑,因此要问程序是什么,程序就是逻辑。
        以下就是自己粗略实现的一个执行命令行管道程序的小shell的代码:
/**  *    简单展示原理,没有错误处理  *    简单展示原理,能用即可  *    这是所谓的“第一个系统”  */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h>  int fork_and_exec(char *cmd, int pin, int pout) {     int pid = fork();     if (pid == 0) {         if (pin != -1) {             dup2 (pin, 0);             close(pin);         }         if (pout != -1) {             dup2 (pout, 1);             close(pout);         }         //exec太麻烦,索性用system了         //但是必须知道system的原理(fork+exec...)         //if (execlp(cmd, NULL) == -1) {         system(cmd); //若是exec且执行成功,就不需要exit了         exit(0);     } else if(pid > 0) {         if (pin != -1)             close(pin);         if (pout != -1)             close(pout);     } else {         //TODO     }     return pid; }  int execute_cmd(char *cmd, int in) {     char *p = cmd;     char *start_cmd = cmd;     int pipefd[2];     while (*p) {         switch (*p) {             case '|':                 *p++ = 0;                 //创建一个管道                 pipe(pipefd);                 //下面的语句执行后,程序分叉,第一叉在当前捕获的命令,第二                 //叉在后面继续解析命令                 //将写管道传给当前捕获的命令用于重定向其stdout                 if (fork_and_exec(start_cmd, in, pipefd[1]) > 0) {                     //将读管道传给将要被捕获的命令用于重定向其stdin                     goto call_forward_pipe_chain;                 }                 break;             default:                 p++;         }     }     fork_and_exec(start_cmd, in, -1);     fflush(stdout);     return 0; call_forward_pipe_chain:     execute_cmd(p, pipefd[0]);     fflush(stdout);     return 0; }  int main(int argc, char **argv) {     while (1) {         char cmd[1024]={0};         int len;         printf("cmd>>");         fflush(stdout);         gets(cmd);         len = strlen(cmd);         if (!strcmp(cmd, "q")) {             fflush(stdout);             printf("done\n");             exit(0);         } else {             execute_cmd(cmd, -1);         }     }     return 0; }

将上述代码编译成mysh之后,执行之:
root@zhaoya-home:~/test# ./mysh 
cmd>>
cmd>>ls /|grep etc
cmd>>etc
cmd>>ls /dev/|grep tty|wc -l
cmd>>69
cmd>>q
done
root@zhaoya-home:~/test# 

以上就是命令行管道的效果。通过使用这种管道的粘合-纵向-加上shell脚本逻辑上的粘合-横向,小巧的UNIX命令就可以结合在一起,完成几乎所有的工作,并且代码利用率极高,高得不可想象啊!如果是一个包罗万象的大程序,每一次你只使用其5%的功能,那么95%的代码将在此次执行中浪费掉,然而小程序互相结合就比较好,由于每个小程序仅仅完成一个功能,因此你只将用到的命令结合在一起即可,提升了代码利用率(UNIX是从小内存时代一路走来的,如今内存都低于白菜价了,却依然勤俭)。

        因此UNIX本质上就是通过粘合小程序而工作的,你可以看到,就算true,false之类的,也是一个合理的命令。



 本文转自 dog250 51CTO博客,原文链接:http://blog.51cto.com/dog250/1268974


相关文章
|
存储 设计模式 SQL
回眸Unix设计哲学
回眸Unix设计哲学
|
Unix iOS开发
UNIX简单的中SVN基本命令行学习
UNIX简单的中SVN基本命令行学习
136 0
UNIX简单的中SVN基本命令行学习
x3d
|
Unix 程序员
回顾一下Unix哲学
Unix哲学是一些先哲们多方位阐述的,有多种说法。可以概括为以下几点: 模块原则:使用简洁的接口拼合简单的部件。 清晰原则:清晰胜于机巧。 组合原则:设计时考虑拼接组合。 分离原则:策略同机制分离,接口同引擎分离。
x3d
795 0
|
Unix 程序员 前端开发
[总结]Unix设计哲学 &lt;&lt;Unix编程艺术&gt;&gt;
转载请注明出处:http://blog.csdn.net/horkychen 学习了第一章关于哲学的部分, 做个汇总.现在对精简设计, 舍弃华而不实是普遍认可的。
1273 0
|
4月前
|
缓存 网络协议 Unix
Linux(UNIX)五种网络I/O模型与IO多路复用
Linux(UNIX)五种网络I/O模型与IO多路复用
110 0