weitom1982

向各位技术前辈学习,学习再学习.
posts - 299, comments - 79, trackbacks - 0, articles - 0
  IT博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

 
Oracle优化经典文章------磁盘I/O和碎片篇

Oracle优化经典文章------磁盘I/O和碎片篇

不平衡的磁盘I/O和撤消会妨碍性能。这里讨论如何正确定位和配置一些与Oracle数据库相关的物理数据文件。 主要讨论以下内容:
[1] 使用磁盘阵列
    选择合理的RAID方式。

[2] 在可用的硬盘之间分布关键数据文件
    要特别注意的文件:system表空间、TEMPORARY表空间、回滚段或UNDO表空间、联机重做日志文件(最好放在最快的磁盘上)、放在ORACLE_HOME文件夹下的关键Oracle文件、经常被访问的表的数据文件、经常被访问的索引的数据文件

    经验总结:把上面提到的数据文件分布在各个可用的磁盘上
              把数据文件和索引文件分开放置
              对于经常连接的表,把他们的数据和索引表空间分开
              把控制文件的多个备份存储到不同的磁盘和控制器上

    避免I/O磁盘争用,用下面的查询确定文件的I/O问题
         select name,phyrds,phywrts,readtim,writetim
         from v$filestat a,v$dbfile b
         where a.file# = b.file#
         order by readtim desc
         说明:在磁盘上的物理写入和读取次数上如果出现很大的差别,就表明肯定有哪个磁盘负载过多!
    如果出现磁盘负载不平衡,可以通过移动数据文件来均衡文件I/O:
         alter tablespace tablespace_name offline;
         $cp /disk1/a.dbf /disk2/a.dbf;
         alter tablespace tablespace_name rename datafile '/disk1/a.dbf' to '/disk2/a.dbf';
         alter tablespace tablespace online;
         $rm /disk1/a.dbf

[3] 使用本地托管的表空间

[4] 在大型表上使用表分区

[5] 使用索引分区
    索引分区与分区表拥有同样的优点。如果正确执行,则通过访问索引的小片段而不是整个表上的索引可以提高性能。

[6] 了解碎片的概念以及如何消除碎片
    经验总结:使用本地托管的统一扩展表空间
              在字典托管的表空间中使用统一的扩展大小(除了SYSTEM表空间外)
              使扩展尺寸是数据库块尺寸的倍数
              不要在字典托管的表空间上设定除0以外的任何PCTINCRREASE值
              把表放在有合适扩展大小的表空间中
              正确设定PCTINCREASE
              避免使用跨越多个不连续的数据文件的位图索引

[7] 使用撤消管理
    如果想使大量的INSERT、UPDATE、DELETE操作速度更快,可以增大日志文件大小(可以增加用于介质恢复的时间),并确保这些文件在最快的磁盘上。

[8] 使用回滚段
    下面查询显示回滚信息:
        select a.name,b.extents,b.rssize,b.xacts,b.waits,b.gets,optsize,stauts
        from v$rollname a,v$rollstat b
        where a.usn = b.usn;
        说明:如果xacts(活动事务)经常超出1,就需要增加回滚段的数目,以避免可能出现的争用。
    把大的事务隔离到它们自己的回滚段上,用下面的语句:
        commit;
        set transation use rollback segment roll_segment_name;
        delete from big_table;
        commit;

[9] 使用更简单的方法:undo表空间
    Oracle 9i新引入的功能。

[10]通过内存排序来减少磁盘I/O

[11]在不同的磁盘和控制器上存放多个控制文件

[12]使用裸设备改善有写操作频繁的数据的I/O

posted @ 2006-03-31 12:01 高山流水 阅读(142) | 评论 (0)编辑 收藏

 

经理人要获得更好的职业机会,在职业发展上持续前进,除了需要不断学习来提高相关的知识和技能之外,还需要培养以下七个习惯:

一、充分地认识自我

一个人能否取得事业上的成功,关键在于是否能准确识别并充分发挥自身的优势。

  同是管理工作,不同领域的管理工作对人有不同的要求。一些管理职位需要有更强的人际影响力,否则将不能带动团队完成目标;而有一些管理工作的性质则对经理人的深度思考和分析能力有着更高的要求,因为他需要更多地充当顾问的角色。因此,要认清自己的才干和优势,在此基础上选择职业方向。

二、树立清晰的职业发展目标

清晰了自身的优势,就要树立明确的职业发展目标。职业发展首先是个人的事,公司只能给予一定的支持和机会。然而,很多中国雇员的习惯是等待公司为其做职业规划,或随机地进行职业选择,这样会使自己走一些弯路或减慢职业发展的速度。

  在任何一个公司里,管理职位都是有限的,只有有明确职业目标并为此不断努力的人,公司才更愿给予他机会,因为这不仅意味这会有好的工作表现,同时也反映了一个人的事业心和追求成功的态度。

三、主动寻求更大的职责

大部分公司在正式给员工晋升之前会对其能力进行全面的考察,而其中的一个考察方式就是在不变动该员工现有的职位和薪酬的前提下给予其与新职位相关的更多的工作职责。

  有的人在这种时候过于急躁,向公司提出各种待遇和职位上的要求,这样做的结果是让公司将他排除在人选之外。因此,在职业发展方面切忌过于急躁,必须有充足的耐心,关注工作职责所能够带来的职业发展机会,而非眼前利益。根据自己所确定的职业发展目标主动寻求更大、更多的工作职责,将无疑为自己的职业发展争取到更好的机会。

四、停止抱怨,承担责任

经理人经常抱怨、指责、推卸责任不但使自己失去改进自身的机会,同时也让周边的人,无论是上司还是其他的同事感到失望从而不愿与其合作。这种态度对下属的影响尤其可怕——团队成员会效仿上司相互抱怨指责,而不是去考虑自己怎样改善相关的工作。

  很显然,一个人在抱怨一个公司的环境和文化氛围阻碍了其发展时,并不意味着他换一家公司就能够获得他所期望的发展——一切取决于他是否能够调整自己的心态和做法。当一个人对周围的人和事横加指责时,他的注意力集中在过去。如果他承担了责任,眼光才会投向未来,才能专注做好计划来达到目标。

五、不可忽略执行力

执行公司战略,带领团队成员达成目标的能力,是公司评估经理人表现的一个主要方面。然而,一些经理人认为执行是低层面的事,他们喜欢提出很多想法却不愿意付诸行动,讲起来头头是道,而行动力不足,到头来没有结果,仍然无法获得公司的认可。如果本职工作都做不好,何谈进一步的职业发展机会?

  因此,执行力是经理人进一步发展的一个核心能力。目标一旦确定,经理人需要迅速规划并组织有效资源,找出切实可行的方法将计划付诸实施,监控过程,及时发现和解决问题,确保结果的达成。

六、关注并帮助下属成长

经理的主要工作除了带领团队完成工作目标之外,还有一个就是培养人才,这包括培养自己的接班人。

  实际上,很多公司在考虑提升一个经理或变换其工作岗位时,首先考虑的问题就是是否有人可以接替他现在的工作。而经理与其他人的不同之处就是通过别人达成工作目标,其下属的工作能力越强,团队的工作结果就越好,同时也说明该经理的工作越出色。因此,能够在达成工作目标的同时帮助其团队成员进行职业发展的经理,也是在为自己的职业发展做进一步的准备。

七、塑造良好的职业操守

良好的职业道德和价值观是经理人获得持续的职业发展的基础,但却经常容易被忽略。由于经理人手中掌控着公司一定的资源,这要求经理人必须有良好的职业操守,清楚地区分公司需求和个人需求,保证公司的利益不受损害。

  经理人还需要客观公正地处理各种事情,不将私人感情和个人喜好搀杂在工作当中,能够做到任人唯贤,知人善用。

  经理人的职业道德和人格力量不仅可以使其在团队中树立起威望和影响力,也让公司更愿意委以重任。“小胜在智,大胜靠德”这句话已经告诉我们职业道德在经理人持续的职业发展中所占的分量。


posted 2005-03-21 23:16:00.0

Feedback

posted @ 2006-03-31 12:01 高山流水 阅读(127) | 评论 (0)编辑 收藏

第一章 引言

1. 本文不适合…
本文不适合想通过本文来装修房子的读者;

本文不适合面向对象编程高手,会浪费你的时间。如果你愿意抽出时间来阅读本文,并提出宝贵的建议,非常感谢!什么?你没有听说过设计模式?那你也敢称高手?

2. 本文适合…
如果你对面向对象编程感兴趣,而又没有时间去读Gang of Four的“Design Patterns Elements of Reusable Object-Oriented Software”(以下简称《设计模式》)。那么,本篇文章将帮助你了解23种设计模式。

我第一次读这本书是在每次晚睡之前,几乎每次都先睡着。《设计模式》以一种严谨,系统化的风格来论述23种设计模式,原书可以说是面向对象编程的一个基础教程,但是要领会其精髓,必须要花费一定的精力。本文的目的是为了帮助你更加方便地理解每一种设计模式,并不想成为原书的替代读物。

本文无意于介绍面向对象的基本知识。因此,假设本文的读者已经对面向对象的封装性、继承性和多态性有足够的了解和认识。并能够认识到可复用的面向对象设计的两个原则:


● 针对接口编程,而不是针对实现编程;

● 优先使用对象组合,而不是类继承。

3. 设计模式是什么?
设计模式概念是由建筑设计师Christopher Alexander提出:“每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心。这样,你就能一次又一次地使用该方案而不必做重复劳动。”上述的定义是对设计模式的广义定义。我们将其应用到面向对象软件的领域内,就形成了对设计模式的狭义定义。

我们可以简单的认为:设计模式就是解决某个特定的面向对象软件问题的特定方法。但严格的来说上述的认识是不准确的,难道面向对象软件中只有区区23个问题?当然不是。

为了能够更加准确地理解这个概念,我们引入另外一个术语:框架(Framework)。框架这个词汇在当今有了各种各样的应用和含义。在设计模式中:框架(Framework)是构成一类特定软件可复用设计的一组相互协作的类。

框架可以认为是一个适用于某个领域的软件包。这个软件包提供了相应领域的各个问题的解决方法。那么,它和设计模式有什么区别?


● 设计模式和框架针对的问题域不同:

设计模式针对面向对象的问题域;框架针对特定业务的问题域;

 

● 设计模式比框架更为抽象:

设计模式在碰到具体问题后,才能产生代码;框架已经可以用代码表示。

● 设计模式是比框架更小的体系结构元素:

框架中可以包括多个设计模式。

Tips:设计模式就像是在武功中基本的招式。我们将这些招式合理地组合起来,就形成套路(框架)。

4. 为什么要用设计模式?
作为程序员都知道良好程序的一个基本标准:高聚合,低耦合。面向对象语言比结构化语言要复杂的多,不良或者没有充分考虑的设计将会导致软件重新设计和开发。然而实际的设计过程中,设计人员更多的考虑如何解决业务问题,对于软件内部结构考虑较少;设计模式则补充了这个缺陷,它主要考虑如何减少对象之间的依赖性,降低耦合程度,使得系统更易于扩展,提高了对象可复用性。因此,设计人员正确的使用设计模式就可以优化系统内部的结构。

第二章 概要简介

在《设计模式》一书中,共包含23个模式。根据目的的不同,将它们分为三类:


● 创建型(Creational):解决如何创建对象的问题。

● 结构型(Structural):解决如何正确的组合类或对象的问题。

● 行为型(Behavioral):解决类或对象之间如何交互和如何分配职责的问题。

Tips:设计模式中经常会用到抽象(Abstract)和具体(Concrete)这两个词。抽象的含义是指它所描述的类(方法)是接口类(方法),具体的含义是指它所描述的类(方法)实现了相应的抽象类(方法)。

第三章 抽象工厂(Abstract factory)

1. 意图
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

2. 分类
创建型模式。

3. 问题是什么?
对于不熟悉这个模式的人都会对工厂(Factory)这个词感到奇怪,为什么会用这个词?之后,我们还会碰到另一个模式——工厂方法(Factory method),所以先解释一下工厂的意义:

房子是由墙,门,窗户,地板,天花板,柱子组成的。如果我们为客户编写一个建造房子的软件,我们会把墙,门,窗户,地板,天花板,柱子看成不同的类:WallClass, DoorClass, WindowClass, CeilingClass, PillarClass。

现在我们建立一个新类A,这个类中有CreateWall(), CreateDoor(), CreateFloor(), CreateCeiling(), CreatePillar()五个方法,每个方法的功能就是创建并返回相应的对象。如果把WallClass, DoorClass, WindowClass, CeilingClass, FloorClass, PillarClass的实例看成产品的话,那么类A就像是一个生产这些产品的工厂。这就是使用工厂这个词的原因。

Tips:A这个名字太糟,如果用HouseFactory会好一些。一般情况下,在你的系统使用某个模式,最好使用模式相应的关键字作为类或者方法的名字的一部分,这样,你的同伴或者系统的代码维护者就会明白你的用意。

我们的软件完成了,客户非常满意。不过,我们的客户想把这个软件出口,他发现一个问题,这个软件太本地化了,建造出来的都是中国式的房屋。因此他希望我们的软件能够建造出不同地域风格的房子。

这就是我们的问题!我们需要重新设计原系统,而且一次完成世界各地不同建筑风格是不可能的。我们会先完成部分风格(客户第一要投放软件的国家的),然后再增加其他的…

4. 解决方法
1) 建立一个抽象工厂(Abstract Factory)类HouseFactory,在这个类中声明:


CreateWall ()

CreateDoor ()

CreateFloor ()

CreateCeiling ()

CreatePillar ()

2) 建立相应的抽象产品(Abstract Product)类集:

Wall, Door, Floor, Ceiling, Pillar

3) 为不同风格建立相应的具体工厂(Concrete Factory)类(不要忘了实现关系),例如:


ChinaHouseFactory : HouseFactory

GreeceHouseFactory : HouseFactory

4) 为不同的风格建立相应的具体产品(Concrete Product)类(实现相应的抽象产品),例如:


ChinaWall : Wall

ChinaDoor : Door


GreeceWall : Wall

GreeceDoor : Door

5. 杂谈
我想你一定明白如何灵活的创建和使用上面的一大堆类:


● 重复最后两个步骤,就可以方便的增加新风格;

● 使用前面两个步骤中声明的抽象类来实现操作。

抽象工厂模式的重点不是声明的那个抽象工厂类,而是它声明的一系列抽象产品类,我们通过使用这些抽象产品类可以操作我们已经实现或者还未实现的具体产品类,并且保证了它们的一致性。

你可能已经发现这个软件不能建造你的两层别墅,因为它没有楼梯。为此,我们要定义Stair抽象类,还要增加CreateStair抽象方法;最重要的是我们要在已经完成的76种风格中增加相应的 Stair类,这可是个大麻烦。确实,抽象工厂模式在适应新的产品方面的能力是比较弱的。这是它的一个缺点。

 

第四章 生成器(Builder)

1. 意图
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

2. 分类
创建型模式。

3. 问题是什么?
在抽象工厂一章中,我们了解到了在全球建房系统中如何支持多种房屋的风格。新的问题是:用户希望看到同样结构下,不同风格房屋的外观。比如:这个房子有一个地板,一个天花板,四面墙,一个门,一个窗户。他希望看到中国风格房屋和希腊风格房屋的相应式样。

4. 解决方法

1) 创建一个生成器(Buider)类:

Class HouseBuider
{
BuildHouse(){}
BuildWall(int){}
BuildDoor(int){}
BuildWindow(int) {}
BuildFloor(){}
BuildCeiling() {}
BuildPillar(){}
GetHouse() { return Null;}
}

在这个类中,我们对每一种房屋组成元素都定义了一个Build方法。并且定义了一个返回构造结果的方法GetHouse。

2) 对每一种风格都定义一个具体的生成器(Concrete Builder)类:

ChinaHouseBuilder: HouseBuilder
GreeceHouseBuilder: HouseBuilder

并且在这些类中重载父类的方法。

3) 还有,各种类型的房屋类:

ChinaHouse
GreeceHouse

5. 如何使用
我们可以用下面的方法使用上面的类来解决我们的问题。

Class HosueSystem
{
object Create( builder HouseBuilder)
{
builder.BuildHouse();
builder.BuildFloor();
builder.BuildCeiling();
builder.BuildWall(1);
builder.BuildWall(2);
builder.BuildWall(3);
builder.BuildWall(4);
builder.Door(1); // 在Wall1上建一个门
builder.Window(2); // 在Wall2上建一个窗户

return builder.GetHouse();
}
}

只要向通过HouseSystem.Create方法传入不同的生成器就可以得到不同风格的结果。

事实上,HouseSystem也是生成器模式的一个参与者,叫做导向者(Director)。注意的问题:


● 生成器(HouseBuilder)类中要包含每一种构件元素的生成方法。比如:楼梯这个元素在某些建筑风格中没有,在其他风格中有,也要加上BuilderStair方法在HouseBuilder中。

● 生成器(HouseBuilder)类不是抽象类,它的每一个生成方法一般情况下什么都不做。这样,具体生成器就不必考虑和它无关的元素的生成方法。

6. 杂谈


1) 也许你有这样的想法:生成器模式似乎过于复杂,可以直接在具体House类中定义CreateHouse方法就可以了,例如:

Class ChinaHouse
{
ChinaHouse CreateHouse()
{ ChinaHouse house;
house = new ChinaHouse();
house.Add(new Floor());
house.Add(new Ceiling());
house.Add(new Wall(1));
house.Add(new Wall(2));
house.Add(new Wall(3));
house.Add(new Wall(4));
house.Add(new Door(1)); // 在Wall1上建一个门
house.Add(new Window(2)); // 在Wall2上建一个窗户

return house;
}
}


而生成器模式至少用了两个类来解决这个问题:导向者类和具体生成器类。

那么,生成器模式好在哪里?答案就是职责分配。生成器模式将一个复杂对象的生成这一职责作了一个很好的分配。它把构造过程放到导向者的方法中,把装配过程放到具体生成器类中。我们看看下面的说明。

2) HouseSystem类(导向者)可以非常精细的来构造House。而这个生成过程,对于产品类(ChinaHouse, GreeceHouse …)和生成器类 (ChinaHouseBuilder, GreeceHouseBuilder)都没有必要关心。具体生成器类则考虑装配元素的问题。

7. 活用
生成器模式可以应用在以下的问题上: 

● 将系统的文档格式转换到其他的格式上(每一种格式的文档都相当于一个产品)。 
● 编译问题(语法分析器是导向者,编译结果是产品)。

posted @ 2006-03-31 11:59 高山流水 阅读(758) | 评论 (0)编辑 收藏

 

Java 2集合框架图

  集合接口:6个接口(短虚线表示),表示不同集合类型,是集合框架的基础。

  抽象类:5个抽象类(长虚线表示),对集合接口的部分实现。可扩展为自定义集合类。

  实现类:8个实现类(实线表示),对接口的具体实现。

  在很大程度上,一旦您理解了接口,您就理解了框架。虽然您总要创建接口特定的实现,但访问实际集合的方法应该限制在接口方法的使用上;因此,允许您更改基本的数据结构而不必改变其它代码。

  · Collection 接口是一组允许重复的对象。

  · Set 接口继承 Collection,但不允许重复,使用自己内部的一个排列机制。

  · List 接口继承 Collection,允许重复,以元素安插的次序来放置元素,不会重新排列。

  · Map接口是一组成对的键-值对象,即所持有的是key-value pairs。Map中不能有重复的key。拥有自己的内部排列机制。

  · 容器中的元素类型都为Object。从容器取得元素时,必须把它转换成原来的类型。


Java 2简化集合框架图

  集合接口

  1.Collection 接口

  用于表示任何对象或元素组。想要尽可能以常规方式处理一组元素时,就使用这一接口。

  (1) 单元素添加、删除操作:

   boolean add(Object o):将对象添加给集合

   boolean remove(Object o): 如果集合中有与o相匹配的对象,则删除对象o

  (2) 查询操作:

   int size() :返回当前集合中元素的数量

   boolean isEmpty() :判断集合中是否有任何元素

   boolean contains(Object o) :查找集合中是否含有对象o

   Iterator iterator() :返回一个迭代器,用来访问集合中的各个元素

  (3) 组操作 :作用于元素组或整个集合

   boolean containsAll(Collection c): 查找集合中是否含有集合c 中所有元素

   boolean addAll(Collection c) : 将集合c 中所有元素添加给该集合

   void clear(): 删除集合中所有元素

   void removeAll(Collection c) : 从集合中删除集合c 中的所有元素

   void retainAll(Collection c) : 从集合中删除集合c 中不包含的元素

  (4) Collection转换为Object数组 :

   Object[] toArray() :返回一个内含集合所有元素的array

   Object[] toArray(Object[] a) :返回一个内含集合所有元素的array。运行期返回的array和参数a的型别相同,需要转换为正确型别。

  此外,您还可以把集合转换成其它任何其它的对象数组。但是,您不能直接把集合转换成基本数据类型的数组,因为集合必须持有对象。

  “斜体接口方法是可选的。因为一个接口实现必须实现所有接口方法,调用程序就需要一种途径来知道一个可选的方法是不是不受支持。如果调用一种可选方法时,一个 UnsupportedOperationException 被抛出,则操作失败,因为方法不受支持。此异常类继承 RuntimeException 类,避免了将所有集合操作放入 try-catch 块。”

  Collection不提供get()方法。如果要遍历Collectin中的元素,就必须用Iterator。

  1.1.AbstractCollection 抽象类

  AbstractCollection 类提供具体“集合框架”类的基本功能。虽然您可以自行实现 Collection 接口的所有方法,但是,除了iterator()和size()方法在恰当的子类中实现以外,其它所有方法都由 AbstractCollection 类来提供实现。如果子类不覆盖某些方法,可选的如add()之类的方法将抛出异常。

  1.2.Iterator 接口

  Collection 接口的iterator()方法返回一个 Iterator。Iterator接口方法能以迭代方式逐个访问集合中各个元素,并安全的从Collection 中除去适当的元素。

  (1) boolean hasNext(): 判断是否存在另一个可访问的元素

     Object next(): 返回要访问的下一个元素。如果到达集合结尾,则抛出NoSuchElementException异常。

  (2) void remove(): 删除上次访问返回的对象。本方法必须紧跟在一个元素的访问后执行。如果上次访问后集合已被修改,方法将抛出IllegalStateException。

  “Iterator中删除操作对底层Collection也有影响。”

  迭代器是 故障快速修复(fail-fast)的。这意味着,当另一个线程修改底层集合的时候,如果您正在用 Iterator 遍历集合,那么,Iterator就会抛出 ConcurrentModificationException (另一种 RuntimeException异常)异常并立刻失败。

  2.List接口

  List 接口继承了 Collection 接口以定义一个允许重复项的有序集合。该接口不但能够对列表的一部分进行处理,还添加了面向位置的操作。

  (1) 面向位置的操作包括插入某个元素或 Collection 的功能,还包括获取、除去或更改元素的功能。在 List 中搜索元素可以从列表的头部或尾部开始,如果找到元素,还将报告元素所在的位置 :

  void add(int index, Object element): 在指定位置index上添加元素element

  boolean addAll(int index, Collection c): 将集合c的所有元素添加到指定位置index

  Object get(int index): 返回List中指定位置的元素

  int indexOf(Object o): 返回第一个出现元素o的位置,否则返回-1

  int lastIndexOf(Object o) :返回最后一个出现元素o的位置,否则返回-1

  Object remove(int index) :删除指定位置上的元素

  Object set(int index, Object element) :用元素element取代位置index上的元素,并且返回旧的元素

  (2) List 接口不但以位置序列迭代的遍历整个列表,还能处理集合的子集:

   ListIterator listIterator() : 返回一个列表迭代器,用来访问列表中的元素

   ListIterator listIterator(int index) : 返回一个列表迭代器,用来从指定位置index开始访问列表中的元素

     List subList(int fromIndex, int toIndex) :返回从指定位置fromIndex(包含)到toIndex(不包含)范围中各个元素的列表视图

  “对子列表的更改(如 add()、remove() 和 set() 调用)对底层 List 也有影响。”

  2.1.ListIterator接口

  ListIterator 接口继承 Iterator 接口以支持添加或更改底层集合中的元素,还支持双向访问。ListIterator没有当前位置,光标位于调用previous和next方法返回的值之间。一个长度为n的列表,有n+1个有效索引值:

  (1) void add(Object o): 将对象o添加到当前位置的前面

   void set(Object o): 用对象o替代next或previous方法访问的上一个元素。如果上次调用后列表结构被修改了,那么将抛出IllegalStateException异常。

  (2) boolean hasPrevious(): 判断向后迭代时是否有元素可访问

   Object previous():返回上一个对象

   int nextIndex(): 返回下次调用next方法时将返回的元素的索引

   int previousIndex(): 返回下次调用previous方法时将返回的元素的索引

  “正常情况下,不用ListIterator改变某次遍历集合元素的方向 — 向前或者向后。虽然在技术上可以实现,但previous() 后立刻调用next(),返回的是同一个元素。把调用 next()和previous()的顺序颠倒一下,结果相同。”

  “我们还需要稍微再解释一下 add() 操作。添加一个元素会导致新元素立刻被添加到隐式光标的前面。因此,添加元素后调用 previous() 会返回新元素,而调用 next() 则不起作用,返回添加操作之前的下一个元素。”

  2.2.AbstractList和AbstractSequentialList抽象类

  有两个抽象的 List 实现类:AbstractList 和 AbstractSequentialList。像 AbstractSet 类一样,它们覆盖了 equals() 和 hashCode() 方法以确保两个相等的集合返回相同的哈希码。若两个列表大小相等且包含顺序相同的相同元素,则这两个列表相等。这里的 hashCode() 实现在 List 接口定义中指定,而在这里实现。

  除了equals()和hashCode(),AbstractList和AbstractSequentialList实现了其余 List 方法的一部分。因为数据的随机访问和顺序访问是分别实现的,使得具体列表实现的创建更为容易。需要定义的一套方法取决于您希望支持的行为。您永远不必亲自提供的是 iterator方法的实现。

  2.3. LinkedList类和ArrayList类

  在“集合框架”中有两种常规的 List 实现:ArrayList 和 LinkedList。使用两种 List 实现的哪一种取决于您特定的需要。如果要支持随机访问,而不必在除尾部的任何位置插入或除去元素,那么,ArrayList 提供了可选的集合。但如果,您要频繁的从列表的中间位置添加和除去元素,而只要顺序的访问列表元素,那么,LinkedList 实现更好。

  “ArrayList 和 LinkedList 都实现 Cloneable 接口,都提供了两个构造函数,一个无参的,一个接受另一个Collection”

  2.3.1. LinkedList类

  LinkedList类添加了一些处理列表两端元素的方法。

  (1) void addFirst(Object o): 将对象o添加到列表的开头

     void addLast(Object o):将对象o添加到列表的结尾

  (2) Object getFirst(): 返回列表开头的元素

     Object getLast(): 返回列表结尾的元素

  (3) Object removeFirst(): 删除并且返回列表开头的元素

     Object removeLast():删除并且返回列表结尾的元素

  (4) LinkedList(): 构建一个空的链接列表

     LinkedList(Collection c): 构建一个链接列表,并且添加集合c的所有元素

  “使用这些新方法,您就可以轻松的把 LinkedList 当作一个堆栈、队列或其它面向端点的数据结构。”

  2.3.2. ArrayList类

  ArrayList类封装了一个动态再分配的Object[]数组。每个ArrayList对象有一个capacity。这个capacity表示存储列表中元素的数组的容量。当元素添加到ArrayList时,它的capacity在常量时间内自动增加。

  在向一个ArrayList对象添加大量元素的程序中,可使用ensureCapacity方法增加capacity。这可以减少增加重分配的数量。

  (1) void ensureCapacity(int minCapacity): 将ArrayList对象容量增加minCapacity

  (2) void trimToSize(): 整理ArrayList对象容量为列表当前大小。程序可使用这个操作减少ArrayList对象存储空间。

  2.3.2.1. RandomAccess接口

  一个特征接口。该接口没有任何方法,不过你可以使用该接口来测试某个集合是否支持有效的随机访问。ArrayList和Vector类用于实现该接口

  3.Set接口

  Set 接口继承 Collection 接口,而且它不允许集合中存在重复项,每个具体的 Set 实现类依赖添加的对象的 equals()方法来检查独一性。Set接口没有引入新方法,所以Set就是一个Collection,只不过其行为不同。

  3.1. Hash表

  Hash表是一种数据结构,用来查找对象。Hash表为每个对象计算出一个整数,称为Hash Code(哈希码)。Hash表是个链接式列表的阵列。每个列表称为一个buckets(哈希表元)。对象位置的计算 index = HashCode % buckets (HashCode为对象哈希码,buckets为哈希表元总数)。

  当你添加元素时,有时你会遇到已经填充了元素的哈希表元,这种情况称为Hash Collisions(哈希冲突)。这时,你必须判断该元素是否已经存在于该哈希表中。

  如果哈希码是合理地随机分布的,并且哈希表元的数量足够大,那么哈希冲突的数量就会减少。同时,你也可以通过设定一个初始的哈希表元数量来更好地控制哈希表的运行。初始哈希表元的数量为 buckets = size * 150% + 1 (size为预期元素的数量)。

  如果哈希表中的元素放得太满,就必须进行rehashing(再哈希)。再哈希使哈希表元数增倍,并将原有的对象重新导入新的哈希表元中,而原始的哈希表元被删除。load factor(加载因子)决定何时要对哈希表进行再哈希。在Java编程语言中,加载因子默认值为0.75,默认哈希表元为101。

  3.2. Comparable接口和Comparator接口

  在“集合框架”中有两种比较接口:Comparable接口和Comparator接口。像String和Integer等Java内建类实现 Comparable接口以提供一定排序方式,但这样只能实现该接口一次。对于那些没有实现Comparable接口的类、或者自定义的类,您可以通过 Comparator接口来定义您自己的比较方式。

  3.2.1. Comparable接口

  在java.lang包中,Comparable接口适用于一个类有自然顺序的时候。假定对象集合是同一类型,该接口允许您把集合排序成自然顺序。

  (1) int compareTo(Object o): 比较当前实例对象与对象o,如果位于对象o之前,返回负值,如果两个对象在排序中位置相同,则返回0,如果位于对象o后面,则返回正值

  在 Java 2 SDK版本1.4中有二十四个类实现Comparable接口。下表展示了8种基本类型的自然排序。虽然一些类共享同一种自然排序,但只有相互可比的类才能排序。

 排序


BigDecimal,BigInteger,Byte, Double, Float,Integer,Long,Short

 按数字大小排序


Character

 按 Unicode 值的数字大小排序


String

 按字符串中字符 Unicode 值排序

  利用Comparable接口创建您自己的类的排序顺序,只是实现compareTo()方法的问题。通常就是依赖几个数据成员的自然排序。同时类也应该覆盖equals()和hashCode()以确保两个相等的对象返回同一个哈希码

3.2.2. Comparator接口

  若一个类不能用于实现java.lang.Comparable,或者您不喜欢缺省的Comparable行为并想提供自己的排序顺序(可能多种排序方式),你可以实现Comparator接口,从而定义一个比较器。

  (1)int compare(Object o1, Object o2): 对两个对象o1和o2进行比较,如果o1位于o2的前面,则返回负值,如果在排序顺序中认为o1和o2是相同的,返回0,如果o1位于o2的后面,则返回正值

  “与Comparable相似,0返回值不表示元素相等。一个0返回值只是表示两个对象排在同一位置。由Comparator用户决定如何处理。如果两个不相等的元素比较的结果为零,您首先应该确信那就是您要的结果,然后记录行为。”

  (2)boolean equals(Object obj): 指示对象obj是否和比较器相等。

  “该方法覆写Object的equals()方法,检查的是Comparator实现的等同性,不是处于比较状态下的对象。”

  3.3. SortedSet接口

  “集合框架”提供了个特殊的Set接口:SortedSet,它保持元素的有序顺序。SortedSet接口为集的视图(子集)和它的两端(即头和尾)提供了访问方法。当您处理列表的子集时,更改视图会反映到源集。此外,更改源集也会反映在子集上。发生这种情况的原因在于视图由两端的元素而不是下标元素指定,所以如果您想要一个特殊的高端元素(toElement)在子集中,您必须找到下一个元素。

  添加到SortedSet实现类的元素必须实现Comparable接口,否则您必须给它的构造函数提供一个Comparator接口的实现。TreeSet类是它的唯一一份实现。

  “因为集必须包含唯一的项,如果添加元素时比较两个元素导致了0返回值(通过Comparable的compareTo()方法或 Comparator的compare()方法),那么新元素就没有添加进去。如果两个元素相等,那还好。但如果它们不相等的话,您接下来就应该修改比较方法,让比较方法和 equals() 的效果一致。”

  (1) Comparator comparator(): 返回对元素进行排序时使用的比较器,如果使用Comparable接口的compareTo()方法对元素进行比较,则返回null

  (2) Object first(): 返回有序集合中第一个(最低)元素

  (3) Object last(): 返回有序集合中最后一个(最高)元素

  (4) SortedSet subSet(Object fromElement, Object toElement): 返回从fromElement(包括)至toElement(不包括)范围内元素的SortedSet视图(子集)

  (5) SortedSet headSet(Object toElement): 返回SortedSet的一个视图,其内各元素皆小于toElement

  (6) SortedSet tailSet(Object fromElement): 返回SortedSet的一个视图,其内各元素皆大于或等于fromElement

  3.4. AbstractSet抽象类

  AbstractSet类覆盖了Object类的equals()和hashCode()方法,以确保两个相等的集返回相同的哈希码。若两个集大小相等且包含相同元素,则这两个集相等。按定义,集的哈希码是集中元素哈希码的总和。因此,不论集的内部顺序如何,两个相等的集会有相同的哈希码。

  3.4.1. Object类

  (1) boolean equals(Object obj): 对两个对象进行比较,以便确定它们是否相同

  (2) int hashCode(): 返回该对象的哈希码。相同的对象必须返回相同的哈希码

  3.5. HashSet类类和TreeSet类

  “集合框架”支持Set接口两种普通的实现:HashSet和TreeSet(TreeSet实现SortedSet接口)。在更多情况下,您会使用 HashSet 存储重复自由的集合。考虑到效率,添加到 HashSet 的对象需要采用恰当分配哈希码的方式来实现hashCode()方法。虽然大多数系统类覆盖了 Object中缺省的hashCode()和equals()实现,但创建您自己的要添加到HashSet的类时,别忘了覆盖 hashCode()和equals()。

  当您要从集合中以有序的方式插入和抽取元素时,TreeSet实现会有用处。为了能顺利进行,添加到TreeSet的元素必须是可排序的。

  3.5.1.HashSet类

  (1) HashSet(): 构建一个空的哈希集

  (2) HashSet(Collection c): 构建一个哈希集,并且添加集合c中所有元素

  (3) HashSet(int initialCapacity): 构建一个拥有特定容量的空哈希集

  (4) HashSet(int initialCapacity, float loadFactor): 构建一个拥有特定容量和加载因子的空哈希集。LoadFactor是0.0至1.0之间的一个数

  3.5.2. TreeSet类

  (1) TreeSet():构建一个空的树集

  (2) TreeSet(Collection c): 构建一个树集,并且添加集合c中所有元素

  (3) TreeSet(Comparator c): 构建一个树集,并且使用特定的比较器对其元素进行排序

  “comparator比较器没有任何数据,它只是比较方法的存放器。这种对象有时称为函数对象。函数对象通常在“运行过程中”被定义为匿名内部类的一个实例。”

  TreeSet(SortedSet s): 构建一个树集,添加有序集合s中所有元素,并且使用与有序集合s相同的比较器排序

  3.6. LinkedHashSet类

  LinkedHashSet扩展HashSet。如果想跟踪添加给HashSet的元素的顺序,LinkedHashSet实现会有帮助。 LinkedHashSet的迭代器按照元素的插入顺序来访问各个元素。它提供了一个可以快速访问各个元素的有序集合。同时,它也增加了实现的代价,因为哈希表元中的各个元素是通过双重链接式列表链接在一起的。

  (1) LinkedHashSet(): 构建一个空的链接式哈希集

  (2) LinkedHashSet(Collection c): 构建一个链接式哈希集,并且添加集合c中所有元素

  (3) LinkedHashSet(int initialCapacity): 构建一个拥有特定容量的空链接式哈希集

  (4) LinkedHashSet(int initialCapacity, float loadFactor): 构建一个拥有特定容量和加载因子的空链接式哈希集。LoadFactor是0.0至1.0之间的一个数

  “为优化HashSet空间的使用,您可以调优初始容量和负载因子。TreeSet不包含调优选项,因为树总是平衡的。”

  4. Map接口

  Map接口不是Collection接口的继承。Map接口用于维护键/值对(key/value pairs)。该接口描述了从不重复的键到值的映射。

  (1) 添加、删除操作:

  Object put(Object key, Object value): 将互相关联的一个关键字与一个值放入该映像。如果该关键字已经存在,那么与此关键字相关的新值将取代旧值。方法返回关键字的旧值,如果关键字原先并不存在,则返回null

  Object remove(Object key): 从映像中删除与key相关的映射

  void putAll(Map t): 将来自特定映像的所有元素添加给该映像

  void clear(): 从映像中删除所有映射

  “键和值都可以为null。但是,您不能把Map作为一个键或值添加给自身。”

  (2) 查询操作:

  Object get(Object key): 获得与关键字key相关的值,并且返回与关键字key相关的对象,如果没有在该映像中找到该关键字,则返回null

  boolean containsKey(Object key): 判断映像中是否存在关键字key

  boolean containsValue(Object value): 判断映像中是否存在值value

  int size(): 返回当前映像中映射的数量

  boolean isEmpty() :判断映像中是否有任何映射

  (3) 视图操作 :处理映像中键/值对组

  Set keySet(): 返回映像中所有关键字的视图集

  “因为映射中键的集合必须是唯一的,您用Set支持。你还可以从视图中删除元素,同时,关键字和它相关的值将从源映像中被删除,但是你不能添加任何元素。”

  Collection values():返回映像中所有值的视图集

  “因为映射中值的集合不是唯一的,您用Collection支持。你还可以从视图中删除元素,同时,值和它的关键字将从源映像中被删除,但是你不能添加任何元素。”

  Set entrySet(): 返回Map.Entry对象的视图集,即映像中的关键字/值对

  “因为映射是唯一的,您用Set支持。你还可以从视图中删除元素,同时,这些元素将从源映像中被删除,但是你不能添加任何元素。”

  4.1. Map.Entry接口

  Map的entrySet()方法返回一个实现Map.Entry接口的对象集合。集合中每个对象都是底层Map中一个特定的键/值对。

  通过这个集合的迭代器,您可以获得每一个条目(唯一获取方式)的键或值并对值进行更改。当条目通过迭代器返回后,除非是迭代器自身的 remove()方法或者迭代器返回的条目的setValue()方法,其余对源Map外部的修改都会导致此条目集变得无效,同时产生条目行为未定义。

  (1) Object getKey(): 返回条目的关键字

  (2) Object getValue(): 返回条目的值

  (3) Object setValue(Object value): 将相关映像中的值改为value,并且返回旧值

  4.2. SortedMap接口

  “集合框架”提供了个特殊的Map接口:SortedMap,它用来保持键的有序顺序。

  SortedMap接口为映像的视图(子集),包括两个端点提供了访问方法。除了排序是作用于映射的键以外,处理SortedMap和处理SortedSet一样。

  添加到SortedMap实现类的元素必须实现Comparable接口,否则您必须给它的构造函数提供一个Comparator接口的实现。TreeMap类是它的唯一一份实现。

  “因为对于映射来说,每个键只能对应一个值,如果在添加一个键/值对时比较两个键产生了0返回值(通过Comparable的 compareTo()方法或通过Comparator的compare()方法),那么,原始键对应值被新的值替代。如果两个元素相等,那还好。但如果不相等,那么您就应该修改比较方法,让比较方法和 equals() 的效果一致。”

  (1) Comparator comparator(): 返回对关键字进行排序时使用的比较器,如果使用Comparable接口的compareTo()方法对关键字进行比较,则返回null

  (2) Object firstKey(): 返回映像中第一个(最低)关键字

  (3) Object lastKey(): 返回映像中最后一个(最高)关键字

  (4) SortedMap subMap(Object fromKey, Object toKey): 返回从fromKey(包括)至toKey(不包括)范围内元素的SortedMap视图(子集)

  (5) SortedMap headMap(Object toKey): 返回SortedMap的一个视图,其内各元素的key皆小于toKey

  (6) SortedSet tailMap(Object fromKey): 返回SortedMap的一个视图,其内各元素的key皆大于或等于fromKey

  4.3. AbstractMap抽象类

  和其它抽象集合实现相似,AbstractMap 类覆盖了equals()和hashCode()方法以确保两个相等映射返回相同的哈希码。如果两个映射大小相等、包含同样的键且每个键在这两个映射中对应的值都相同,则这两个映射相等。映射的哈希码是映射元素哈希码的总和,其中每个元素是Map.Entry接口的一个实现。因此,不论映射内部顺序如何,两个相等映射会报告相同的哈希码。

  4.4. HashMap类和TreeMap类

  “集合框架”提供两种常规的Map实现:HashMap和TreeMap (TreeMap实现SortedMap接口)。在Map 中插入、删除和定位元素,HashMap 是最好的选择。但如果您要按自然顺序或自定义顺序遍历键,那么TreeMap会更好。使用HashMap要求添加的键类明确定义了hashCode()和equals()的实现。

  这个TreeMap没有调优选项,因为该树总处于平衡状态。

  4.4.1. HashMap类

  为了优化HashMap空间的使用,您可以调优初始容量和负载因子。

  (1) HashMap(): 构建一个空的哈希映像

  (2) HashMap(Map m): 构建一个哈希映像,并且添加映像m的所有映射

  (3) HashMap(int initialCapacity): 构建一个拥有特定容量的空的哈希映像

  (4) HashMap(int initialCapacity, float loadFactor): 构建一个拥有特定容量和加载因子的空的哈希映像

  4.4.2. TreeMap类

  TreeMap没有调优选项,因为该树总处于平衡状态。

  (1) TreeMap():构建一个空的映像树

  (2) TreeMap(Map m): 构建一个映像树,并且添加映像m中所有元素

  (3) TreeMap(Comparator c): 构建一个映像树,并且使用特定的比较器对关键字进行排序

  (4) TreeMap(SortedMap s): 构建一个映像树,添加映像树s中所有映射,并且使用与有序映像s相同的比较器排序

  4.5. LinkedHashMap类

  LinkedHashMap扩展HashMap,以插入顺序将关键字/值对添加进链接哈希映像中。象LinkedHashSet一样,LinkedHashMap内部也采用双重链接式列表。

  (1) LinkedHashMap(): 构建一个空链接哈希映像

  (2) LinkedHashMap(Map m): 构建一个链接哈希映像,并且添加映像m中所有映射

  (3) LinkedHashMap(int initialCapacity): 构建一个拥有特定容量的空的链接哈希映像

  (4) LinkedHashMap(int initialCapacity, float loadFactor): 构建一个拥有特定容量和加载因子的空的链接哈希映像

  (5) LinkedHashMap(int initialCapacity, float loadFactor,

  boolean accessOrder): 构建一个拥有特定容量、加载因子和访问顺序排序的空的链接哈希映像

  “如果将accessOrder设置为true,那么链接哈希映像将使用访问顺序而不是插入顺序来迭

  代各个映像。每次调用get或者put方法时,相关的映射便从它的当前位置上删除,然后放到链接式映像列表的结尾处(只有链接式映像列表中的位置才会受到影响,哈希表元则不受影响。哈希表映射总是待在对应于关键字的哈希码的哈希表元中)。”

  “该特性对于实现高速缓存的“删除最近最少使用”的原则很有用。例如,你可以希望将最常访问的映射保存在内存中,并且从数据库中读取不经常访问的对象。当你在表中找不到某个映射,并且该表中的映射已经放得非常满时,你可以让迭代器进入该表,将它枚举的开头几个映射删除掉。这些是最近最少使用的映射。”

  (6) protected boolean removeEldestEntry(Map.Entry eldest): 如果你想删除最老的映射,则覆盖该方法,以便返回true。当某个映射已经添加给映像之后,便调用该方法。它的默认实现方法返回false,表示默认条件下老的映射没有被删除。但是你可以重新定义本方法,以便有选择地在最老的映射符合某个条件,或者映像超过了某个大小时,返回true。

  4.6. WeakHashMap类

  WeakHashMap是Map的一个特殊实现,它使用WeakReference(弱引用)来存放哈希表关键字。使用这种方式时,当映射的键在 WeakHashMap 的外部不再被引用时,垃圾收集器会将它回收,但它将把到达该对象的弱引用纳入一个队列。WeakHashMap的运行将定期检查该队列,以便找出新到达的弱应用。当一个弱引用到达该队列时,就表示关键字不再被任何人使用,并且它已经被收集起来。然后WeakHashMap便删除相关的映射。

  (1) WeakHashMap(): 构建一个空弱哈希映像

  (2) WeakHashMap(Map t): 构建一个弱哈希映像,并且添加映像t中所有映射

  (3) WeakHashMap(int initialCapacity): 构建一个拥有特定容量的空的弱哈希映像

  (4) WeakHashMap(int initialCapacity, float loadFactor): 构建一个拥有特定容量和加载因子的空的弱哈希映像

  4.6. IdentityHashMap类

  IdentityHashMap也是Map的一个特殊实现。在这个类中,关键字的哈希码不应该由hashCode()方法来计算,而应该由 System.identityHashCode方法进行计算(即使已经重新定义了hashCode方法)。这是Object.hashCode根据对象的内存地址来计算哈希码时使用的方法。另外,为了对各个对象进行比较,IdentityHashMap将使用==,而不使用equals方法。

  换句话说,不同的关键字对象,即使它们的内容相同,也被视为不同的对象。IdentityHashMap类可以用于实现对象拓扑结构转换 (topology-preserving object graph transformations)(比如实现对象的串行化或深度拷贝),在进行转换时,需要一个“节点表”跟踪那些已经处理过的对象的引用。即使碰巧有对象相等,“节点表”也不应视其相等。另一个应用是维护代理对象。比如,调试工具希望在程序调试期间维护每个对象的一个代理对象。

  “IdentityHashMap类不是一般意义的Map实现!它的实现有意的违背了Map接口要求通过equals方法比较对象的约定。这个类仅使用在很少发生的需要强调等同性语义的情况。”

  (1) IdentityHashMap (): 构建一个空的全同哈希映像,默认预期最大尺寸为21

  “预期最大尺寸是映像期望把持的键/值映射的最大数目”

  (2) IdentityHashMap (Map m): 构建一个全同哈希映像,并且添加映像m中所有映射

  (3) IdentityHashMap (int expectedMaxSize): 构建一个拥有预期最大尺寸的空的全同哈希映像。放置超过预期最大尺寸的键/值映射时,将引起内部数据结构的增长,有时可能很费时

posted @ 2006-03-31 11:58 高山流水 阅读(129) | 评论 (0)编辑 收藏

概述:CVS是一个C/S系统,多个开发人员通过一个中心版本控制系统来记录文件版本,从而达到保证文件同步的目的。

CVS服务器(文件版本库)
/ |
(版 本 同 步)
/ |
开发者1 开发者2 开发者3

以下是本文主要内容:开发人员可以主要挑选2, 6看就可以了,CVS的管理员则更需要懂的更多一些

1.CVS环境初始化:CVS环境的搭建 管理员
2.CVS的日常使用:日常开发中最常用的CVS命令, 开发人员 管理员
3.CVS的分支开发:项目按照不同进度和目标并发进行 管理员
4.CVS的用户认证:通过SSH的远程用户认证,安全,简单 管理员
5.CVSWEB:CVS的WEB访问界面大大提高代码版本比较的效率 管理员
6.CVS TAG:将$Id$加入代码注释中,方便开发过程的跟踪 开发人员
7.CVS vs VSS: CVS和Virsual SourceSafe的比较

一个系统20%的功能往往能够满足80%的需求,CVS也不例外,以下是CVS最常用的功能,
可能用到的还不到它全部命令选项的10%,更多的功能请在实际应用过程中体会,学习过程
中应该是用多少,学多少,用到了再学也不迟。


CVS环境初始化
============

环境设置:指定CVS库的路径CVSROOT
tcsh
setenv CVSROOT /path/to/cvsroot
bash
CVSROOT=/path/to/cvsroot ; export CVSROOT

后面还提到远程CVS服务器的设置:
CVSROOT=:ext:$USER@test.server.address#port:/path/to/cvsroot CVS_RSH=ssh; export CVSROOT CVS_RSH

初始化:CVS版本库的初始化。
cvs init

一个项目的首次导入
cvs import -m ""write some comments here"" project_name vendor_tag release_tag
执行后:会将所有源文件及目录导入到/path/to/cvsroot/project_name目录下
vender_tag: 开发商标记
release_tag: 版本发布标记

项目导出:将代码从CVS库里导出
cvs checkout project_name
cvs 将创建project_name目录,并将最新版本的源代码导出到相应目录中。这个checkout和Virvual SourceSafe中的check
out不是一个概念,相对于Virvual SourceSafe的check out是cvs update, check in是cvs commit。

CVS的日常使用
=============

注意:第一次导出以后,就不是通过cvs checkout来同步文件了,而是要进入刚才cvs checkout
project_name导出的project_name目录下进行具体文件的版本同步(添加,修改,删除)操作。

将文件同步到最新的版本:
cvs update
不制定文件名,cvs将同步所有子目录下的文件,也可以制定某个文件名/目录进行同步
cvs update file_name
最好每天开始工作前或将自己的工作导入到CVS库里前都要做一次,并养成“先同步 后修改”的习惯,和Virvual
SourceSafe不同,CVS里没有文件锁定的概念,所有的冲突是在commit之前解决,
如果你修改过程中,有其他人修改并commit到了CVS库中,CVS会通知你文件冲突,并
自动将冲突部分用
>>>>>>
content on cvs server
<<<<<<
content in your file
>>>>>>
标记出来,由你确认冲突内容的取舍。
版本冲突一般是在多个人修改一个文件造成的,但这种项目管理上的问题不应该指望由CVS来解决。

确认修改写入到CVS库里:
cvs commit -m ""write some comments here"" file_name

注意:CVS的很多动作都是通过cvs
commit进行最后确认并修改的,最好每次只修改一个文件。在确认的前,还需要用户填写修改注释,
以帮助其他开发人员了解修改的原因。如果不用写-m ""comments""而直接确认``cvs commit file_name``
的话,cvs会自动调用系统缺省的文字编辑器(一般是vi)要求你写入注释。
注释的质量很重要:所以不仅必须要写,而且必须写一些比较有意义的内容:以方便其他开发人员能够很好的理解
不好的注释,很难让其他的开发人员快速的理解:比如: -m ""bug fixed"" 甚至 -m """"
好的注释,甚至可以用中文: -m ""在用户注册过程中加入了Email地址校验""

修改某个版本注释:每次只确认一个文件到CVS库里是一个很好的习惯,但难免有时候忘了
指定文件名,把多个文件以同样注释commit到CVS库里了,以下命令可以允许你修改某个
文件某个版本的注释:
cvs admin -m 1.3:""write some comments here"" file_name

添加文件
创建好新文件后,比如:touch new_file
cvs add new_file
注意:对于图片,Word文档等非纯文本的项目,需要使用cvs add -b选项,否则有可能出现文件被破坏的情况
比如:cvs add -kb new_file.gif
然后确认修改并注释
cvs ci -m ""write some comments here""

删除文件:
将某个源文件物理删除后,比如:rm file_name
cvs rm file_name
然后确认修改并注释
cvs ci -m ""write some comments here""
以上面前2步合并的方法为:
cvs rm -f file_name
cvs ci -m ""why delete file""

注意:很多cvs命令都有缩写形式:commit=>ci; update=>up; checkout=>co; remove=>rm;


添加目录:
cvs add dir_name

查看修改历史:cvs log file_name
cvs history file_name

查看当前文件不同版本的区别
cvs diff -r1.3 -r1.5 file_name
查看当前文件(可能已经修改了)和库中相应文件的区别
cvs diff file_name
cvs的web界面提供了更方便的定位文件修改和比较版本区别的方法,具体安装设置请看后面的cvsweb使用

正确的通过CVS恢复旧版本的方法:
如果用cvs update -r1.2 file.name
这个命令是给file.name加一个STICK TAG: ""1.2"" ,虽然你的本意只是想将它恢复到1.2版本
正确的恢复版本的方法是:cvs update -p -r1.2 file_name >file_name
如果不小心已经加成STICK TAG的话:用cvs update -A 解决

移动文件:文件重命名
cvs里没有cvs move或cvs rename,因为这两个操作是先cvs remove old_file_name,然后cvs add new_file_name实现的。

删除,移动目录:
最方便的方法是让管理员直接移动,删除CVSROOT里相应目录(因为CVS一个项目下的子目录都是独立的,
移动到$CVSROOT目录下都可以作为新的独立项目:好比一颗树,其实砍下任意一枝都能独立存活),
对目录进行了修改后,要求其开发人员重新导出项目cvs checkout project_name 或者用
cvs update -dP同步。

CVS Branch:项目多分支同步开发
=============================

确认版本里程碑:多个文件各自版本号不一样,项目到一定阶段,可以给所有文件统一指定
一个阶段里程碑版本号,方便以后按照这个阶段里程碑版本号导出项目,同时也是项目的
多个分支开发的基础。
cvs tag release_1_0

开始一个新的里程碑:
cvs commit -r 2 标记所有文件开始进入2.x的开发

注意:CVS里的revsion和软件包的发布版本可以没有直接的关系。但所有文件使用和发布版本一致的版本号比较有助于维护。

在开发项目的2.x版本的时候发现1.x有问题,但2.x又不敢用,则从先前标记的里程碑:release_1_0导出一个分支release_1_0_patch
cvs rtag -b -r release_1_0 release_1_0_patch proj_dir

一些人先在另外一个目录下导出release_1_0_patch这个分支:解决1.0中的紧急问题,
cvs checkout -r release_1_0_patch
而其他人员仍旧在项目的主干分支2.x上开发

在release_1_0_patch上修正错误后,标记一个1.0的错误修正版本号
cvs tag release_1_0_patch_1

如果2.0认为这些错误修改在2.0里也需要,也可以在2.0的开发目录下合并release_1_0_patch_1中的修改到当前代码中:
cvs update -j release_1_0_patch_1

CVS的远程认证:通过SSH远程访问CVS
================================

使用cvs本身的远程认证很麻烦,需要定义服务器和用户组,用户名,设置密码等,而且不安全,
因此和系统本地帐号认证并通过SSH传输是比较好的办法,通过在客户机的/etc/profile里设置一下内容:
CVSROOT=:ext:$USER@test.server.address#port:/path/to/cvsroot CVS_RSH=ssh; export CVSROOT CVS_RSH
所有客户机所有本地用户都可以映射到CVS服务器相应同名帐号了。

如果CVS所在服务器的SSH端口不在缺省的22,或者和客户端与CVS服务器端SSH缺省端口不一致,有时候设置了:
:ext:$USER@test.server.address#port:/path/to/cvsroot

仍然不行,比如有以下错误信息:
ssh: test.server.address#port: Name or service not known
cvs [checkout aborted]: end of file from server (consult above messages if any)

解决的方法是做一个脚本指定端口转向(不能使用alias,会出找不到文件错误):
创建一个/usr/bin/ssh_cvs文件:
#!/usr/bin/sh
/path/to/ssh -p 34567 ""$@""
然后:chmod +x /usr/bin/ssh_cvs
并CVS_RSH=ssh_cvs; export CVS_RSH

注意:port是指相应服务器SSH的端口,不是cvs pserver的端口

CVSWEB:提高程序员比较文件修改效率
================================

CVSWEB就是CVS的WEB界面,可以大大提高程序员定位修改的效率:
使用的样例可以看:
http://www.freebsd.org/cgi/cvsweb.cgi

CVSWEB的下载:CVSWEB从最初的版本已经演化出很多功能界面更丰富的版本,这个是个人感觉觉得安装设置比较方便的:
http://www.spaghetti-code.de/software/linux/cvsweb/

下载解包:
tar zxf cvsweb.tgz
把配置文件cvsweb.conf放到安全的地方(比如和apache的配置放在同一个目录下),
修改:cvsweb.cgi让CGI找到配置文件:
$config = $ENV{''CVSWEB_CONFIG''} || ''/path/to/apache/conf/cvsweb.conf'';

转到/path/to/apache/conf下并修改cvsweb.conf:

1.修改CVSROOT路径设置:
%CVSROOT = (
''Development'' => ''/path/to/cvsroot'', #<==修改指向本地的CVSROOT
);
2.缺省不显示已经删除的文档:
""hideattic"" => ""1"",#<==缺省不显示已经删除的文档
3.在配置文件cvsweb.conf中还可以定制页头的描述信息,你可以修改$long_intro成你需要的文字

CVSWEB可不能随便开放给所有用户,因此需要使用WEB用户认证:
先生成 passwd:
/path/to/apache/bin/htpasswd -c cvsweb.passwd user

修改httpd.conf: 增加

AuthName ""CVS Authorization""
AuthType Basic
AuthUserFile /path/to/cvsweb.passwd
require valid-user


CVS TAGS: who? when?
====================

将$Id$ 加在程序文件开头的注释里是一个很好的习惯,cvs能够自动解释更新其中的内容成:file_name version time
user_name 的格式,比如:cvs_card.txt,v 1.1 2002/04/05 04:24:12 chedong
Exp,可以这些信息了解文件的最后修改人和修改时间

几个常用的缺省文件:
default.php
/*
* Copyright (c) 2002 Company Name.
* $Header$
*/

?>

====================================
Default.java: 注意文件头一般注释用 /* 开始 JAVADOC注释用 /** 开始的区别
/*
* Copyright (c) 2002 Company Name.
* $Header$
*/

package com.netease;

import java.io;

/**
* comments here
*/
public class Default {
/**
*
* @param
* @return
*/
public toString() {

}
}

====================================
default.pl:
#!/usr/bin/perl -w
# Copyright (c) 2002 Company Name.
# $Header$

# file comments here

use strict;

CVS vs VSS 
===========

CVS没有文件锁定模式,VSS在check out同时,同时记录了文件被导出者锁定。

CVS是update commit, VSS是check out check in

在CVS中,标记自动更新功能缺省是打开的,这样也带来一个潜在的问题,就是不用-kb方式添加binary文件的话在cvs自动更新时可能会导致文件失效。

Virsual SourceSafe中这个功能称之为Keyword
Explaination,缺省是关闭的,需要通过OPITION打开,并指定需要进行源文件关键词扫描的类型:*.txt,*.java,*.html...

对于Virsual SourceSafe和CVS都通用的TAG有:
$Header$
$Author$
$Date$
$Revision$

尽量使用通用的关键词保证代码在CVS和VSS都能方便的跟踪。

 

相关资源:

CVS HOME:
http://www.cvshome.org

CVS FAQ:
http://www.loria.fr/~molli/cvs-index.html

相关网站:
http://directory.google.com/Top/Computers/Software/Configuration_Management/Tools/Concurrent_Versions_System/

CVS 免费书:
http://cvsbook.red-bean.com/

CVS 命令的速查卡片:
http://www.refcards.com/about/cvs.html

posted @ 2006-03-31 11:56 高山流水 阅读(103) | 评论 (0)编辑 收藏

Linux 系统命令及其使用详解(大全)
(来源: 中国系统分析员)

  cat cd
  chmod chown
  cp cut
  名称:cat
  使用权限:所有使用者
  使用方式:cat [-AbeEnstTuv] [--help] [--version] fileName
  说明:把档案串连接后传到基本输出(萤幕或加 > fileName 到另一个档案)
  参数:
  -n 或 --number 由 1 开始对所有输出的行数编号
  -b 或 --number-nonblank 和 -n 相似,只不过对于空白行不编号
  -s 或 --squeeze-blank 当遇到有连续两行以上的空白行,就代换为一行的空白行
  -v 或 --show-nonprinting
  范例:
  cat -n textfile1 > textfile2 把 textfile1 的档案内容加上行号后输入 textfile2 这个档案里
  cat -b textfile1 textfile2 >> textfile3 把 textfile1 和 textfile2 的档案内容加上行号(空白行不加)之后将内容附加到 textfile3

  名称:cd
  使用权限:所有使用者
  使用方式:cd [dirName]
  说明:变换工作目录至 dirName。 其中 dirName 表示法可为绝对路径或相对路径。若目录名称省略,则变换至使用者的 home directory (也就是刚 login 时所在的目录).另外,"~" 也表示为 home directory 的意思,"." 则是表示目前所在的目录,".." 则表示目前目录位置的上一层目录。
  范例:跳到 /usr/bin/:
  cd /usr/bin

  跳到自己的 home directory:
  cd ~

  跳到目前目录的上上两层:
  cd ../..

  指令名称:chmod
  使用权限:所有使用者
  使用方式:chmod [-cfvR] [--help] [--version] mode file...
  说明:Linux/Unix 的档案存取权限分为三级:档案拥有者,群组,其他。利用 chmod 可以藉以控制档案如何被他人所存取。
  把计:
  mode:权限设定字串,格式如下:[ugoa...][[+-=][rwxX]...][,...],其中u 表示该档案的拥有者,g 表示与该档案的拥有者属于同一个群体(group)者,o 表示其他以外的人,a 表示这三者皆是。
  + 表示增加权限,- 表示取消权限,= 表示唯一设定权限。
  r 表示可读取,w 表示可写入,x 表示可执行,X 表示只有当该档案是个子目录或者该档案已经被设定过为可执行。
  -c:若该档案权限确实已经更改,才显示其更改动作
  -f:若该档案权限无法被更改也不要显示错误讯息
  -v:显示权限变更的详细资料
  -R:对目前目录下的所有档案与子目录进行相同的权限变更(即以递回的方式逐个变更)
  --help:显示辅助说明
  --version:显示版本
  范例 :将档案 file1.txt 设为所有人皆可读取:
  chmod ugo+r file1.txt

  将档案 file1.txt 设为所有人皆可读取:
  chmod a+r file1.txt

  将档案 file1.txt 与 file2.txt 设为该档案拥有者,与其所属同一个群体者可写入,但其他以外的人则不可写入:
  chmod ug+w,o-w file1.txt file2.txt

  将 ex1.py 设定为只有该档案拥有者可以执行:
  chmod u+x ex1.py

  将目前目录下的所有档案与子目录皆设为任何人可读取:
  chmod -R a+r *

  此外chmod也可以用数字来表示权限如 chmod 777 file
  语法为:chmod abc file
  其中a,b,c各为一个数字,分别表示User,Group,及Other的权限。

  r=4,w=2,x=1
  若要rwx属性则4+2+1=7;
  若要rw-属性则4+2=6;
  若要r-x属性则4+1=7。

  范例:
  chmod a=rwx file
  和
  chmod 777 file
  效果相同
  chmod ug=rwx,o=x file
  和
  chmod 771 file
  效果相同
  若用chmod 4755 filename可使此程式具有root的权限

  指令名称:chown
  使用权限:root
  使用方式:chmod [-cfhvR] [--help] [--version] user[:group] file...

  说明:Linux/Unix 是多人多工作业系统,所有的档案皆有拥有者。利用 chown 可以将档案的拥有者加以改变。一般来说,这个指令只有是由系统管理者(root)所使用,一般使用者没有权限可以改变别人的档案拥有者,也没有权限可以自己的档案拥有者改设为别人。只有系统管理者(root)才有这样的权限。
  把计:
  user:新的档案拥有者的使用者 IDgroup:新的档案拥有者的使用者群体(group)-c:若该档案拥有者确实已经更改,才显示其更改动作-f:若该档案拥有者无法被更改也不要显示错误讯息-h:只对于连结(link)进行变更,而非该 link 真正指向的档案-v:显示拥有者变更的详细资料-R:对目前目录下的所有档案与子目录进行相同的拥有者变更(即以递回的方式逐个变更)--help:显示辅助说明--version:显示版本
  范例:
  将档案 file1.txt 的拥有者设为 users 群体的使用者 jessie:
  chown jessie:users file1.txt

  将目前目录下的所有档案与子目录的拥有者皆设为 users 群体的使用者 lamport:
  chmod -R lamport:users *

  名称:cp
  使用权限:所有使用者
  使用方式:
  cp [options] source dest
  cp [options] source... directory
  说明:将一个档案拷贝至另一档案,或将数个档案拷贝至另一目录。
  把计:
  -a 尽可能将档案状态,权限等资料都照原状予以复制。
  -r 若 source 中含有目录名,则将目录下之档案亦皆依序拷贝至目的地。
  -f 若目的地已经有相同档名的档案存在,则在复制前先予以删除再行复制。
  范例:
  将档案 aaa 复制(已存在),并命名为 bbb:
  cp aaa bbb

  将所有的C语言程式拷贝至 Finished 子目录中:
  cp *.c Finished

  名称:cut
  使用权限:所有使用者
  用法:cut -cnum1-num2 filename
  说明:显示每行从开头算起 num1 到 num2 的文字。
  范例:
  shell>> cat example
  test2
  this is test1
  shell>> cut -c0-6 example ## print 开头算起前 6 个字元
  test2
  this i

  用法:find
  使用说明:
  将档案系统内符合 expression 的档案列出来。你可以指要档案的名称,类别,时间,大小,权限等不同资讯的组合,只有完全相符的才会被列出来。
  find 根据下列规则判断 path 和 expression,在命令列上第一个 - ( ) , ! 之前的部份为 path,之后的是 expression。如果 path 是空字串则使用目前路径,如果 expression 是空字串则使用 -print 为预设 expression
  expression 中可使用的选项有二三十个之多,在此只介绍最常用的部份。
  -mount, -xdev:只检查和指定目录在同一个档案系统下的档案,避免列出其它档案系统中的档案
  -amin n:在过去 n 分钟内被读取过
  -anewer file:比档案 file 更晚被读取过的档案
  -atime n:在过去 n 天过读取过的档案
  -cmin n:在过去 n 分钟内被修改过
  -cnewer file :比档案 file 更新的档案
  -ctime n:在过去 n 天过修改过的档案
  -empty:空的档案-gid n or -group name:gid 是 n 或是 group 名称是 name
  -ipath p, -path p:路径名称符合 p 的档案,ipath 会忽略大小写
  -name name, -iname name:档案名称符合 name 的档案。iname 会忽略大小写
  -size n:档案大小 是 n 单位,b 代表 512 位元组的区块,c 表示字元数,k 表示 kilo bytes,w 是二个位元组。-type c:档案类型是 c 的档案。
  d: 目录
  c: 字型装置档案
  b: 区块装置档案
  p: 具名贮列
  f: 一般档案
  l: 符号连结
  s: socket
  -pid n:process id 是 n 的档案

  你可以使用 ( ) 将运算式分隔,并使用下列运算。
  exp1 -and exp2
  ! expr
  -not expr
  exp1 -or exp2
  exp1, exp2
  范例:
  将目前目录及其子目录下所有延伸档名是 c 的档案列出来。
  # find . -name "*.c"
  将目前目录其其下子目录中所有一般档案列出
  # find . -ftype f
  将目前目录及其子目录下所有最近 20 分钟内更新过的档案列出
  # find . -ctime -20

  名称:less
  使用权限:所有使用者
  使用方式:
  less [Option] filename
  说明:
  less 的作用与 more 十分相似,都可以用来浏览文字档案的内容,不同的是 less 允许使用者往回卷动
  以浏览已经看过的部份,同时因为 less 并未在一开始就读入整个档案,因此在遇上大型档案的开启时,会比一般的文书编辑器(如 vi)来的快速。
  范例:


  指令名称:ln
  使用权限:所有使用者
  使用方式:ln [options] source dist,其中 option 的格式为:
  [-bdfinsvF] [-S backup-suffix] [-V {numbered,existing,simple}]
  [--help] [--version] [--]

  说明:Linux/Unix 档案系统中,有所谓的连结(link),我们可以将其视为档案的别名,而连结又可分为两种:硬连结(hard link)与软连结(symbolic link),硬连结的意思是一个档案可以有多个名称,而软连结的方式则是产生一个特殊的档案,该档案的内容是指向另一个档案的位置。硬连结是存在同一个档案系统中,而软连结却可以跨越不同的档案系统。

  ln source dist 是产生一个连结(dist)到 source,至于使用硬连结或软链结则由参数决定。

  不论是硬连结或软链结都不会将原本的档案复制一份,只会占用非常少量的磁碟空间。

  -f:链结时先将与 dist 同档名的档案删除-d:允许系统管理者硬链结自己的目录-i:在删除与 dist 同档名的档案时先进行询问-n:在进行软连结时,将 dist 视为一般的档案-s:进行软链结(symbolic link)-v:在连结之前显示其档名-b:将在链结时会被覆写或删除的档案进行备份-S SUFFIX:将备份的档案都加上 SUFFIX 的字尾-V METHOD:指定备份的方式--help:显示辅助说明--version:显示版本
  范例:
  将档案 yy 产生一个 symbolic link:zz
  ln -s yy zz
  将档案 yy 产生一个 hard link:zz
  ln yy xx

  名称:locate
  使用权限:所有使用者
  使用方式: locate [-q] [-d ] [--database=]
  locate [-r ] [--regexp=]
  locate [-qv] [-o ] [--output=]
  locate [-e ] [-f ] <[-l ] [-c]
  <[-U ] [-u]>
  locate [-Vh] [--version] [--help]
  说明:
  locate 让使用者可以很快速的搜寻档案系统内是否有指定的档案。其方法是先建立一个包括系统内所有档案名称及路径的资料库,之后当寻找时就只需查询这个资料库,而不必实际深入档案系统之中了。在一般的 distribution 之中,资料库的建立都被放在 contab 中自动执行。
  一般使用者在使用时只要用
  # locate your_file_name
  的型式就可以了。 参数:
  -u
  -U
  建立资料库,-u 会由根目录开始,-U 则可以指定开始的位置。
  -e

  将排除在寻找的范围之外。
  -l
  如果 是 1.则启动安全模式。在安全模式下,使用者不会看到权限无法看到的档案。这会始速度减慢,因为 locate 必须至实际的档案系统中取得档案的权限资料。
  -f
  将特定的档案系统排除在外,例如我们没有到理要把 proc 档案系统中的档案放在资料库中。
  -q
  安静模式,不会显示任何错误讯息。
  -n
  至多显示 个输出。
  -r
  使用正规运算式 做寻找的条件。
  -o
  指定资料库存的名称。
  -d
  指定资料库的路径
  -h
  显示辅助讯息
  -v
  显示更多的讯息
  -V
  显示程式的版本讯息 范例:

  locate chdrv:寻找所有叫 chdrv 的档案
  locate -n 100 a.out:寻找所有叫 a.out 的档案,但最多只显示 100 个
  locate -u:建立资料库

  名称:ls
  使用权限:所有使用者
  使用方式:ls [-alrtAFR] [name...]
  说明:显示指定工作目录下之内容(列出目前工作目录所含之档案及子目录)。
  -a 显示所有档案及目录 (ls内定将档案名或目录名称开头为"."的视为隐藏档,不会列出)
  -l 除档案名称外,亦将档案型态,权限,拥有者,档案大小等资讯详细列出
  -r 将档案以相反次序显示(原定依英文字母次序)
  -t 将档案依建立时间之先后次序列出
  -A 同 -a ,但不列出 "." (目前目录) 及 ".." (父目录)
  -F 在列出的档案名称后加一符号;例如可执行档则加 "*", 目录则加 "/"
  -R 若目录下有档案,则以下之档案亦皆依序列出
  范例:
  列出目前工作目录下所有名称是 s 开头的档案,愈新的排愈后面:
  ls -ltr s*
  将 /bin 目录以下所有目录及档案详细资料列出:
  ls -lR /bin
  列出目前工作目录下所有档案及目录;目录于名称后加 "/", 可执行档于名称后加 "*":
  ls -AF

  名称:more
  使用权限:所有使用者
  使用方式:more [-dlfpcsu] [-num] [+/pattern] [+linenum] [fileNames..]
  说明:类似 cat ,不过会以一页一页的显示方便使用者逐页阅读,而最基本的指令就是按空白键(space)就往下一页显示,按 b 键就会往回(back)一页显示,而且还有搜寻字串的功能(与 vi 相似),使用中的说明文件,请按 h 。
  参数:-num 一次显示的行数
  -d 提示使用者,在画面下方显示 [Press space to continue, q to quit.] ,如果使用者按错键,则会显示 [Press h for instructions.] 而不是 哔 声
  -l 取消遇见特殊字元 ^L(送纸字元)时会暂停的功能
  -f 计算行数时,以实际上的行数,而非自动换行过后的行数(有些单行字数太长的会被扩展为两行或两行以上)
  -p 不以卷动的方式显示每一页,而是先清除萤幕后再显示内容
  -c 跟 -p 相似,不同的是先显示内容再清除其他旧资料
  -s 当遇到有连续两行以上的空白行,就代换为一行的空白行
  -u 不显示下引号 (根据环境变数 TERM 指定的 terminal 而有所不同)
  +/ 在每个档案显示前搜寻该字串(pattern),然后从该字串之后开始显示
  +num 从第 num 行开始显示
  fileNames 欲显示内容的档案,可为复数个数
  范例:
  more -s testfile 逐页显示 testfile 之档案内容,如有连续两行以上空白行则以一行空白行显示。
  more +20 testfile 从第 20 行开始显示 testfile 之档案内容。

  名称:mv
  使用权限:所有使用者
  使用方式:
  mv [options] source dest
  mv [options] source... directory
  说明:将一个档案移至另一档案,或将数个档案移至另一目录。
  参数:-i 若目的地已有同名档案,则先询问是否覆盖旧档。
  范例:
  将档案 aaa 更名为 bbb:
  mv aaa bbb
  将所有的C语言程式移至 Finished 子目录中:
  mv -i *.c

  名称:rm
  使用权限:所有使用者
  使用方式:rm [options] name...
  说明:删除档案及目录。
  把计:
  -i 删除前逐一询问确认。
  -f 即使原档案属性设为唯读,亦直接删除,无需逐一确认。
  -r 将目录及以下之档案亦逐一删除。
  范例:
  删除所有C语言程式档;删除前逐一询问确认:
  rm -i *.c
  将 Finished 子目录及子目录中所有档案删除:
  rm -r Finished

  名称:rmdir
  使用权限:于目前目录有适当权限的所有使用者
  使用方式: rmdir [-p] dirName
  说明: 删除空的目录。
  参数: -p 是当子目录被删除后使它也成为空目录的话,则顺便一并删除。
  范例:
  将工作目录下,名为 AAA 的子目录删除:
  rmdir AAA

  在工作目录下的 BBB 目录中,删除名为 Test 的子目录。若 Test 删除后,BBB 目录成为空目录,则 BBB 亦予删除。
  rmdir -p BBB/Test

  名称:split
  使用权限:所有使用者
  使用方式:split [OPTION] [INPUT [PREFIX]]说明:
  将一个档案分割成数个。而从 INPUT 分割输出成固定大小的档案,其档名依序为 PREFIXaa, PREFIXab...;PREFIX 预设值为 `x。若没有 INPUT 档或为 `-,则从标准输入读进资料。
  匡兜:
  -b, --bytes=SIZE
  SIZE 值为每一输出档案的大小,单位为 byte。
  -C, --line-bytes=SIZE
  每一输出档中,单行的最大 byte 数。
  -l, --lines=NUMBER
  NUMBER 值为每一输出档的列数大小。
  -NUMBER
  与 -l NUMBER 相同。
  --verbose
  于每个输出档被开启前,列印出侦错资讯到标准错误输出。
  --help
  显示辅助资讯然后离开。
  --version
  列出版本资讯然后离开。
  SIZE 可加入单位: b 代表 512, k 代表 1K, m 代表 1 Meg。
  范例:
  PostgresSQL 大型资料库备份与回存:
  因 Postgres 允许表格大过你系统档案的最大容量,所以要将表格 dump 到单一的档案可能会有问题,使用 split进行档案分割。
  % pg_dump dbname | split -b 1m - filename.dump.

  重新载入
  % createdb dbname
  % cat filename.dump.* | pgsql dbname

  名称:touch
  使用权限:所有使用者
  使用方式:
  touch [-acfm]
  [-r reference-file] [--file=reference-file]
  [-t MMDDhhmm[[CC]YY][.ss]]
  [-d time] [--date=time] [--time={atime,access,use,mtime,modify}]
  [--no-create] [--help] [--version]
  file1 [file2 ...]

  说明:
  touch 指令改变档案的时间记录。 ls -l 可以显示档案的时间记录。
  参数:
  a 改变档案的读取时间记录。
  m 改变档案的修改时间记录。
  c 假如目的档案不存在,不会建立新的档案。与 --no-create 的效果一样。
  f 不使用,是为了与其他 unix 系统的相容性而保留。
  r 使用参考档的时间记录,与 --file 的效果一样。
  d 设定时间与日期,可以使用各种不同的格式。
  t 设定档案的时间记录,格式与 date 指令相同。
  --no-create 不会建立新档案。
  --help 列出指令格式。
  --version 列出版本讯息。
  范例:
  最简单的使用方式,将档案的时候记录改为现在的时间。若档案不存在,系统会建立一个新的档案。
  touch file
  touch file1 file2
  将 file 的时间记录改为 5 月 6 日 18 点 3 分,公元两千年。时间的格式可以参考 date 指令,至少需输入 MMDDHHmm ,就是月日时与分。
  touch -c -t 05061803 file
  touch -c -t 050618032000 file
  将 file 的时间记录改变成与 referencefile 一样。
  touch -r referencefile file

  将 file 的时间记录改成 5 月 6 日 18 点 3 分,公元两千年。时间可以使用 am, pm 或是 24 小时的格式,日期可以使用其他格式如 6 May 2000 。
  touch -d "6:03pm" file
  touch -d "05/06/2000" file
  touch -d "6:03pm 05/06/2000" file

  名称:at
  使用权限:所有使用者
  使用方式:at -V [-q queue] [-f file] [-mldbv] TIME
  说明:at 可以让使用者指定在 TIME 这个特定时刻执行某个程式或指令,TIME 的格式是 HH:MM其中的 HH 为小时,MM 为分钟,甚至你也可以指定 am, pm, midnight, noon, teatime(就是下午 4 点锺)等口语词。
  如果想要指定超过一天内的时间,则可以用 MMDDYY 或者 MM/DD/YY 的格式,其中 MM 是分钟,DD 是第几日,YY 是指年份。另外,使用者甚至也可以使用像是 now + 时间间隔来弹性指定时间,其中的时间间隔可以是 minutes, hours, days, weeks
  另外,使用者也可指定 today 或 tomorrow 来表示今天或明天。当指定了时间并按下 enter 之后,at 会进入交谈模式并要求输入指令或程式,当你输入完后按下 ctrl+D 即可完成所有动作,至于执行的结果将会寄回你的帐号中。
  把计:
  -V:印出版本编号
  -q:使用指定的伫列(Queue)来储存,at 的资料是存放在所谓的 queue 中,使用者可以同时使用多个 queue,而 queue 的编号为 a, b, c... z 以及 A, B, ... Z 共 52 个
  -m:即使程式/指令执行完成后没有输出结果, 也要寄封信给使用者
  -f file:读入预先写好的命令档。使用者不一定要使用交谈模式来输入,可以先将所有的指定先写入档案后再一次读入
  -l:列出所有的指定 (使用者也可以直接使用 atq 而不用 at -l)
  -d:删除指定 (使用者也可以直接使用 atrm 而不用 at -d)
  -v:列出所有已经完成但尚未删除的指定
  例子:
  三天后的下午 5 点锺执行 /bin/ls:
  at 5pm + 3 days /bin/ls

  三个星期后的下午 5 点锺执行 /bin/ls:
  at 5pm + 2 weeks /bin/ls

  明天的 17:20 执行 /bin/date:
  at 17:20 tomorrow /bin/date
  1999 年的最后一天的最后一分钟印出 the end of world !
  at 23:59 12/31/1999 echo the end of world !

  名称:cal
  使用权限:所有使用者
  使用方式:cal [-mjy] [month [year]]
  说明:
  显示日历。若只有一个参数,则代表年份(1-9999),显示该年的年历。年份必须全部写出:``cal 89\ 将不会是显示 1989 年的年历。使用两个参数,则表示月份及年份。若没有参数则显示这个月的月历。
  1752 年 9 月第 3 日起改用西洋新历,因这时大部份的国家都采用新历,有 10 天被去除,所以该月份的月历有些不同。在此之前为西洋旧历。
  匡兜:
  -m:以星期一为每周的第一天方式显示。
  -j:以凯撒历显示,即以一月一日起的天数显示。
  -y:显示今年年历。
  范例:
  cal:显示本月的月历。
  [root@mylinux /root]# date
  Tue Aug 15 08:00:18 CST 2000
  [root@mylinux /root]# cal
  August 2000
  Su Mo Tu We Th Fr Sa
  1 2 3 4 5
  6 7 8 9 10 11 12
  13 14 15 16 17 18 19
  20 21 22 23 24 25 26
  27 28 29 30 31

  [root@mylinux /root]#

  cal 2001:显示公元 2001 年年历。
  [root@mylinux /root]# cal 2001
  2001

  January February March
  Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa
  1 2 3 4 5 6 1 2 3 1 2 3
  7 8 9 10 11 12 13 4 5 6 7 8 9 10 4 5 6 7 8 9 10
  14 15 16 17 18 19 20 11 12 13 14 15 16 17 11 12 13 14 15 16 17
  21 22 23 24 25 26 27 18 19 20 21 22 23 24 18 19 20 21 22 23 24
  28 29 30 31 25 26 27 28 25 26 27 28 29 30 31

  April May June
  Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa
  1 2 3 4 5 6 7 1 2 3 4 5 1 2
  8 9 10 11 12 13 14 6 7 8 9 10 11 12 3 4 5 6 7 8 9
  15 16 17 18 19 20 21 13 14 15 16 17 18 19 10 11 12 13 14 15 16
  22 23 24 25 26 27 28 20 21 22 23 24 25 26 17 18 19 20 21 22 23
  29 30 27 28 29 30 31 24 25 26 27 28 29 30

  July August September
  Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa
  1 2 3 4 5 6 7 1 2 3 4 1
  8 9 10 11 12 13 14 5 6 7 8 9 10 11 2 3 4 5 6 7 8
  15 16 17 18 19 20 21 12 13 14 15 16 17 18 9 10 11 12 13 14 15
  22 23 24 25 26 27 28 19 20 21 22 23 24 25 16 17 18 19 20 21 22
  29 30 31 26 27 28 29 30 31 23 24 25 26 27 28 29
  30
  October November December
  Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa
  1 2 3 4 5 6 1 2 3 1
  7 8 9 10 11 12 13 4 5 6 7 8 9 10 2 3 4 5 6 7 8
  14 15 16 17 18 19 20 11 12 13 14 15 16 17 9 10 11 12 13 14 15
  21 22 23 24 25 26 27 18 19 20 21 22 23 24 16 17 18 19 20 21 22
  28 29 30 31 25 26 27 28 29 30 23 24 25 26 27 28 29
  30 31

  [root@mylinux /root]#

  cal 5 2001:显示公元 2001 年 5 月月历。

  [root@mylinux /root]# cal 5 2001
  May 2001
  Su Mo Tu We Th Fr Sa
  1 2 3 4 5
  6 7 8 9 10 11 12
  13 14 15 16 17 18 19
  20 21 22 23 24 25 26
  27 28 29 30 31

  [root@mylinux /root]#

  cal -m:以星期一为每周的第一天方式,显示本月的月历。

  [root@mylinux /root]# cal -m
  August 2000
  Mo Tu We Th Fr Sa Su
  1 2 3 4 5 6
  7 8 9 10 11 12 13
  14 15 16 17 18 19 20
  21 22 23 24 25 26 27
  28 29 30 31

  [root@mylinux /root]#

  cal -jy:以一月一日起的天数显示今年的年历。

  [root@mylinux /root]# cal -jy
  2000

  January February
  Sun Mon Tue Wed Thu Fri Sat Sun Mon Tue Wed Thu Fri Sat
  1 32 33 34 35 36
  2 3 4 5 6 7 8 37 38 39 40 41 42 43
  9 10 11 12 13 14 15 44 45 46 47 48 49 50
  16 17 18 19 20 21 22 51 52 53 54 55 56 57
  23 24 25 26 27 28 29 58 59 60
  30 31
  March April
  Sun Mon Tue Wed Thu Fri Sat Sun Mon Tue Wed Thu Fri Sat
  61 62 63 64 92
  65 66 67 68 69 70 71 93 94 95 96 97 98 99
  72 73 74 75 76 77 78 100 101 102 103 104 105 106
  79 80 81 82 83 84 85 107 108 109 110 111 112 113
  86 87 88 89 90 91 114 115 116 117 118 119 120
  121
  May June
  Sun Mon Tue Wed Thu Fri Sat Sun Mon Tue Wed Thu Fri Sat
  122 123 124 125 126 127 153 154 155
  128 129 130 131 132 133 134 156 157 158 159 160 161 162
  135 136 137 138 139 140 141 163 164 165 166 167 168 169
  142 143 144 145 146 147 148 170 171 172 173 174 175 176
  149 150 151 152 177 178 179 180 181 182

  July August
  Sun Mon Tue Wed Thu Fri Sat Sun Mon Tue Wed Thu Fri Sat
  183 214 215 216 217 218
  184 185 186 187 188 189 190 219 220 221 222 223 224 225
  191 192 193 194 195 196 197 226 227 228 229 230 231 232
  198 199 200 201 202 203 204 233 234 235 236 237 238 239
  205 206 207 208 209 210 211 240 241 242 243 244
  212 213
  September October
  Sun Mon Tue Wed Thu Fri Sat Sun Mon Tue Wed Thu Fri Sat
  245 246 275 276 277 278 279 280 281
  247 248 249 250 251 252 253 282 283 284 285 286 287 288
  254 255 256 257 258 259 260 289 290 291 292 293 294 295
  261 262 263 264 265 266 267 296 297 298 299 300 301 302
  268 269 270 271 272 273 274 303 304 305

  November December
  Sun Mon Tue Wed Thu Fri Sat Sun Mon Tue Wed Thu Fri Sat
  306 307 308 309 336 337
  310 311 312 313 314 315 316 338 339 340 341 342 343 344
  317 318 319 320 321 322 323 345 346 347 348 349 350 351
  324 325 326 327 328 329 330 352 353 354 355 356 357 358
  331 332 333 334 335 359 360 361 362 363 364 365
  366

  [root@mylinux /root]#

  名称:crontab
  使用权限:所有使用者
  使用方式:
  crontab [ -u user ] filecrontab [ -u user ] { -l | -r | -e }
  说明:
  crontab 是用来让使用者在固定时间或固定间隔执行程式之用,换句话说,也就是类似使用者的时程表。-u user 是指设定指定 user 的时程表,这个前提是你必须要有其权限(比如说是 root)才能够指定他人的时程表。如果不使用 -u user 的话,就是表示设定自己的时程表。
  参数:

  -e:执行文字编辑器来设定时程表,内定的文字编辑器是 VI,如果你想用别的文字编辑器,则请先设定 VISUAL 环境变数来指定使用那个文字编辑器(比如说 setenv VISUAL joe)
  -r:删除目前的时程表
  -l:列出目前的时程表

  时程表的格式如下:
  f1 f2 f3 f4 f5 program

  其中 f1 是表示分钟,f2 表示小时,f3 表示一个月份中的第几日,f4 表示月份,f5 表示一个星期中的第几天。program 表示要执行的程式。
  当 f1 为 * 时表示每分钟都要执行 program,f2 为 * 时表示每小时都要执行程式,其余类推
  当 f1 为 a-b 时表示从第 a 分钟到第 b 分钟这段时间内要执行,f2 为 a-b 时表示从第 a 到第 b 小时都要执行,其余类推
  当 f1 为 */n 时表示每 n 分钟个时间间隔执行一次,f2 为 */n 表示每 n 小时个时间间隔执行一次,其余类推
  当 f1 为 a, b, c,... 时表示第 a, b, c,... 分钟要执行,f2 为 a, b, c,... 时表示第 a, b, c...个小时要执行,其余类推
  使用者也可以将所有的设定先存放在档案 file 中,用 crontab file 的方式来设定时程表。
  例子:
  每月每天每小时的第 0 分钟执行一次 /bin/ls:
  0 7 * * * /bin/ls

  在 12 月内, 每天的早上 6 点到 12 点中,每隔 20 分钟执行一次 /usr/bin/backup:
  0 6-12/3 * 12 * /usr/bin/backup

  周一到周五每天下午 5:00 寄一封信给 alex@domain.name:
  0 17 * * 1-5 mail -s "hi" alex@domain.name < /tmp/maildata

  每月每天的午夜 0 点 20 分, 2 点 20 分, 4 点 20 分....执行 echo "haha"
  20 0-23/2 * * * echo "haha"

  注意:
  当程式在你所指定的时间执行后,系统会寄一封信给你,显示该程式执行的内容,若是你不希望收到这样的信,请在每一行空一格之后加上 > /dev/null 2>&1 即可。

  名称:date
  使用权限:所有使用者
  使用方式:
  date [-u] [-d datestr] [-s datestr] [--utc] [--universal] [--date=datestr] [--set=datestr] [--help] [--version] [+FORMAT] [MMDDhhmm[[CC]YY][.ss]]

  说明:
  date 可以用来显示或设定系统的日期与时间,在显示方面,使用者可以设定欲显示的格式,格式设定为一个加号后接数个标记,其中可用的标记列表如下:
  时间方面:

  %:印出 %
  %n:下一行
  %t:跳格
  %H:小时(00..23)
  %I:小时(01..12)
  %k:小时(0..23)
  %l:小时(1..12)
  %M:分钟(00..59)
  %p:显示本地 AM 或 PM
  %r:直接显示时间 (12 小时制,格式为 hh:mm:ss [AP]M)
  %s:从 1970 年 1 月 1 日 00:00:00 UTC 到目前为止的秒数
  %S:秒(00..61)
  %T:直接显示时间 (24 小时制)
  %X:相当于 %H:%M:%S
  %Z:显示时区

  日期方面:
  %a:星期几 (Sun..Sat)
  %A:星期几 (Sunday..Saturday)
  %b:月份 (Jan..Dec)
  %B:月份 (January..December)
  %c:直接显示日期与时间
  %d:日 (01..31)
  %D:直接显示日期 (mm/dd/yy)
  %h:同 %b
  %j:一年中的第几天 (001..366)
  %m:月份 (01..12)
  %U:一年中的第几周 (00..53) (以 Sunday 为一周的第一天的情形)
  %w:一周中的第几天 (0..6)
  %W:一年中的第几周 (00..53) (以 Monday 为一周的第一天的情形)
  %x:直接显示日期 (mm/dd/yy)
  %y:年份的最后两位数字 (00.99)
  %Y:完整年份 (0000..9999)

  若是不以加号作为开头,则表示要设定时间,而时间格式为 MMDDhhmm[[CC]YY][.ss],其中 MM 为月份,DD 为日,hh 为小时,mm 为分钟,CC 为年份前两位数字,YY 为年份后两位数字,ss 为秒数
  把计:
  -d datestr:显示 datestr 中所设定的时间 (非系统时间)
  --help:显示辅助讯息
  -s datestr:将系统时间设为 datestr 中所设定的时间
  -u:显示目前的格林威治时间
  --version:显示版本编号
  例子:
  显示时间后跳行,再显示目前日期:
  date +%T%n%D
  显示月份与日数:
  date +%B %d

  显示日期与设定时间(12:34:56):
  date --date 12:34:56
  注意:
  当你不希望出现无意义的 0 时(比如说 1999/03/07),则可以在标记中插入 - 符号,比如说 date +%-H:%-M:%-S 会把时分秒中无意义的 0 给去掉,像是原本的 08:09:04 会变为 8:9:4。另外,只有取得权限者(比如说 root)才能设定系统时间。
  当你以 root 身分更改了系统时间之后,请记得以 clock -w 来将系统时间写入 CMOS 中,这样下次重新开机时系统时间才会持续抱持最新的正确值。
  
  名称:sleep
  使用权限:所有使用者
  使用方式:sleep [--help] [--version] number[smhd]
  说明:sleep 可以用来将目前动作延迟一段时间
  参数说明:
  --help:显示辅助讯息
  --version:显示版本编号
  number:时间长度,后面可接 s,m,h 或 d
  其中 s 为秒,m 为 分钟,h 为小时,d 为日数
  例子:
  显示目前时间后延迟 1 分钟,之后再次显示时间:
  date;sleep 1m;date

  名称: time
  使用权限: 所有使用者
  使用方式: time [options] COMMAND [arguments]
  说明:
  time 指令的用途,在于量测特定指令执行时所需消耗的时间及系统资源等资讯。例如 CPU 时间,记忆体,输入输出等等。需要特别注意的是,部分资讯在 Linux 上显示不出来。这是因为在 Linux 上部分资源的分配函式与 time 指令所预设的方式并不相同,以致于 time 指令无法取得这些资料。

  把计:
  -o or --output=FILE
  设定结果输出档。这个选项会将 time 的输出写入 所指定的档案中。如果档案已经存在,系统将覆写其内容。
  -a or --append
  配合 -o 使用,会将结果写到档案的末端,而不会覆盖掉原来的内容。
  -f FORMAT or --format=FORMAT
  以 FORMAT 字串设定显示方式。当这个选项没有被设定的时候,会用系统预设的格式。不过你可以用环境变数 time 来设定这个格式,如此一来就不必每次登入系统都要设定一次。
  一般设定上,你可以用
  \t
  表示跳栏,或者是用
  \n
  表示换行。每一项资料要用 % 做为前导。如果要在字串中使用百分比符号,就用.(学过C语言的人大概会觉得很熟悉)
  time 指令可以显示的资源有四大项,分别是:

  Time resources
  Memory resources
  IO resources
  Command info
  详细的内容如下:
  Time Resources
  E 执行指令所花费的时间,格式是:[hour]:minute:second。请注意这个数字并不代表实际的 CPU 时间。
  e 执行指令所花费的时间,单位是秒。请注意这个数字并不代表实际的 CPU 时间。
  S 指令执行时在核心模式(kernel mode)所花费的时间,单位是秒。
  U 指令执行时在使用者模式(user mode)所花费的时间,单位是秒。
  P 执行指令时 CPU 的占用比例。其实这个数字就是核心模式加上使用者模式的 CPU 时间除以总时间。

  Memory Resources
  M 执行时所占用的实体记忆体的最大值。单位是 KB
  t 执行时所占用的实体记忆体的平均值,单位是 KB
  K 执行程序所占用的记忆体总量(stack+data+text)的平均大小,单位是 KB
  D 执行程序的自有资料区(unshared data area)的平均大小,单位是 KB
  p 执行程序的自有堆叠(unshared stack)的平均大小,单位是 KB
  X 执行程序间共享内容(shared text)的平均值,单位是 KB
  Z 系统记忆体页的大小,单位是 byte。对同一个系统来说这是个常数


  IO Resources
  F 此程序的主要记忆体页错误发生次数。所谓的主要记忆体页错误是指某一记忆体页已经置换到置换档(swap file)中,而且已经分配给其他程序。此时该页的内容必须从置换档里再读出来。
  R 此程序的次要记忆体页错误发生次数。所谓的次要记忆体页错误是指某一记忆体页虽然已经置换到置换档中,但尚未分配给其他程序。此时该页的内容并未被破坏,不必从置换档里读出来
  W 此程序被交换到置换档的次数
  c 此程序被强迫中断(像是分配到的 CPU 时间耗尽)的次数
  w 此程序自愿中断(像是在等待某一个 I/O 执行完毕,像是磁碟读取等等)的次数
  I 此程序所输入的档案数
  O 此程序所输出的档案数
  r 此程序所收到的 Socket Message
  s 此程序所送出的 Socket Message
  k 此程序所收到的信号 ( Signal )数量

  Command Info
  C 执行时的参数以及指令名称
  x 指令的结束代码 ( Exit Status )

  -p or --portability
  这个选项会自动把显示格式设定成为:
  real %e
  user %U
  sys %S
  这么做的目的是为了与 POSIX 规格相容。
  -v or --verbose
  这个选项会把所有程式中用到的资源通通列出来,不但如一般英文语句,还有说明。对不想花时间去熟习格式设定或是刚刚开始接触这个指令的人相当有用。
  范例:
  利用下面的指令
  time -v ps -aux

  我们可以获得执行 ps -aux 的结果和所花费的系统资源。如下面所列的资料:
  USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
  root 1 0.0 0.4 1096 472 ? S Apr19 0:04 init
  root 2 0.0 0.0 0 0 ? SW Apr19 0:00 [kflushd]
  root 3 0.0 0.0 0 0 ? SW Apr19 0:00 [kpiod]
  ......
  root 24269 0.0 1.0 2692 996 pts/3 R 12:16 0:00 ps -aux

  Command being timed: "ps -aux"
  User time (seconds): 0.05
  System time (seconds): 0.06
  Percent of CPU this job got: 68%
  Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.16
  Average shared text size (kbytes): 0
  Average unshared data size (kbytes): 0
  Average stack size (kbytes): 0
  Average total size (kbytes): 0
  Maximum resident set size (kbytes): 0
  Average resident set size (kbytes): 0
  Major (requiring I/O) page faults: 238
  Minor (reclaiming a frame) page faults: 46
  Voluntary context switches: 0
  Involuntary context switches: 0
  Swaps: 0
  File system inputs: 0
  File system outputs: 0
  Socket messages sent: 0
  Socket messages received: 0
  Signals delivered: 0
  Page size (bytes): 4096
  Exit status: 0

  使用权限: 所有使用者
  使用方式: uptime [-V]
  说明: uptime 提供使用者下面的资讯,不需其他参数:
  现在的时间
  系统开机运转到现在经过的时间
  连线的使用者数量
  最近一分钟,五分钟和十五分钟的系统负载
  参数: -V 显示版本资讯。
  范例: uptime
  其结果为:
  10:41am up 5 days, 10 min, 1 users, load average: 0.00, 0.00, 1.99

  名称:chfn
  使用权限:所有使用者
  用法:shell>> chfn
  说明:提供使用者更改个人资讯,用于 finger and mail username
  范例:
  shell>> chfn
  Changing finger information for user
  Password: [del]
  Name[]:Johnney Huang ### 提供 finger 时的资料
  Office[]:NCCU
  Office Phone[]: [del]
  Home Phone[]: [del]

  名称:chsh
  使用权限:所有使用者
  用法:shell>> chsh
  说明:更改使用者 shell 设定
  范例:
  shell>> chsh
  Changing fihanging shell for user1
  Password: [del]
  New shell [/bin/tcsh]: ### [是目前使用的 shell]
  [del]

  shell>> chsh -l ### 展示 /etc/shells 档案内容
  /bin/bash
  /bin/sh
  /bin/ash
  /bin/bsh
  /bin/tcsh
  /bin/csh


  ” finger [返回]
  名称: finger
  使用权限: 所有使用者
  使用方式: finger [options] user[@address]
  说明:finger 可以让使用者查询一些其他使用者的资料。会列出来的资料有:
  Login Name
  User Name
  Home directory
  Shell
  Login status
  mail status
  .plan
  .project
  .forward

  其中 .plan ,.project 和 .forward 就是使用者在他的 Home Directory 里的 .plan , .project 和 .forward 等档案里的资料。如果没有就没有。finger 指令并不限定于在同一伺服器上查询,也可以寻找某一个远端伺服器上的使用者。只要给一个像是 E-mail address 一般的地址即可。
  把计:
  -l
  多行显示。
  -s
  单行显示。这个选项只显示登入名称,真实姓名,终端机名称,闲置时间,登入时间,办公室号码及电话号码。如果所查询的使用者是远端伺服器的使用者,这个选项无效。

  范例:下列指令可以查询本机管理员的资料:
  finger root

  其结果如下:
  Login: root Name: root
  Directory: /root Shell: /bin/bash
  Never logged in.
  No mail.
  No Plan.

  名称:last
  使用权限:所有使用者
  使用方式:shell>> last [options]
  说明:显示系统开机以来获是从每月初登入者的讯息
  把计:
  -R 省略 hostname 的栏位
  -num 展示前 num 个
  username 展示 username 的登入讯息
  tty 限制登入讯息包含终端机代号
  范例:

  shell>> last -R -2
  johnney pts/1 Mon Aug 14 20:42 still logged in
  johnney pts/0 Mon Aug 14 19:59 still logged in

  wtmp begins Tue Aug 1 09:01:10 2000 ### /var/log/wtmp


  shell>> last -2 minery
  minery pts/0 140.119.217.115 Mon Aug 14 18:37 - 18:40 (00:03)
  minery pts/0 140.119.217.115 Mon Aug 14 17:22 - 17:24 (00:02)
  wtmp begins Tue Aug 1 09:01:10 2000


  名称:login
  这个命令都不会就不要干算了!呵呵我也不在这里多费笔墨耽误大家美好青春了^_^

  名称:passwd
  使用权限:所有使用者
  使用方式:passwd [-k] [-l] [-u [-f]] [-d] [-S] [username]
  说明:用来更改使用者的密码
  参数:
  -k
  -l
  -u
  -f
  -d 关闭使用者的密码认证功能, 使用者在登入时将可以不用输入密码, 只有具备 root 权限的使用者方可使用.
  -S 显示指定使用者的密码认证种类, 只有具备 root 权限的使用者方可使用.
  [username] 指定帐号名称.

  名称:who
  使用权线:所有使用者都可使用
  使用方式:who - [husfV] [user]
  说明:显示系统中有那些使用者正在上面,显示的资料包含了使用者 ID,使用的终端机,从那边连上来的,上线时间,呆滞时间,CPU 使用量,动作等等。
  把计:
  -h:不要显示标题列
  -u:不要显示使用者的动作/工作
  -s:使用简短的格式来显示
  -f:不要显示使用者的上线位置
  -V:显示程式版本

  名称:/etc/aliases
  使用权限:系统管理者
  使用方式: 请用 newaliases 更新资料库
  说明:
  sendmail 会使用一个在 /etc/aliases 中的档案做使用者名称转换的动作。当 sendmail 收到一个要送给 xxx 的信时,它会依据 aliases档的内容送给另一个使用者。这个功能可以创造一个只有在信件系统内才有效的使用者。例如 mailing list 就会用到这个功能,在 mailinglist 中,我们可能会创造一个叫 redlinux@link.ece.uci.edu 的 mailinglist,但实际上并没有一个叫 redlinux 的使用者。实际 aliases 档的内容是将送给这个使用者的信都收给 mailing list 处理程式负责分送的工作。
  /etc/aliases 是一个文字模式的档案,sendmail 需要一个二进位格式的 /etc/aliases.db。newaliases 的功能传是将 /etc/aliases 转换成一个 sendmail 所能了解的资料库。范例:
  # newaliases

  下面命令会做相同的事,
  # sendmail -bi

  相关命令:
  mail, mailq, newaliases, sendmail

  ” mail [返回]

  名称:mail
  使用权限:所有使用者
  使用方式:mail [-iInv] [-s subject] [-c cc-addr] [-b bcc-addr] user1 [user 2 ...]
  说明:
  mail 不仅只是一个指令, mail 还是一个电子邮件程式,不过利用 mail 来读信的人应该很少吧!对于系统管理者来说 mail 就很有用,因为管理者可以用 mail 写成 script ,定期寄一些备忘录提醒系统的使用者。


  参数:
  i 忽略 tty 的中断讯号。 (interrupt)
  I 强迫设成互动模式。 (Interactive)
  v 列印出讯息,例如送信的地点,状态等等。 (verbose)
  n 不读入 mail.rc 设定档。
  s 邮件标题。
  c cc 邮件地址。
  b bcc 邮件地址。
  范例:
  将信件送给一个或以上的电子邮件地址,由于没有加入其他的选项,使用者必须输入标题与信件的内容等。而 user2 没有主机位置,就会送给邮件伺服器的 user2 使用者。
  mail user1@email.address
  mail user1@email.address user2

  将 mail.txt 的内容寄给 user2 同时 cc 给 user1 。如果将这一行指令设成 cronjob 就可以定时将备忘录寄给系统使用者。
  mail -s 标题 -c user1 user2 < mail.txt

  指令:mesg
  使用权限:所有使用者
  使用方式:mesg [y|n]
  说明 : 决定是否允许其他人传讯息到自己的终端机介面
  把计 :
  y:允许讯息传到终端机介面上。
  n:不允许讯息传到终端机介面上 。
  如果没有设定,则讯息传递与否则由终端机界面目前状态而定。
  例子:
  改变目前讯息设定,改成不允许讯息传到终端机介面上:
  mesg n

  与 mesg 相关的指令有: talk,write,wall。


  名称:/etc/aliases
  使用权限:系统管理者
  使用方式: newaliases
  说明:
  sendmail 会使用一个在 /etc/aliases 中的档案做使用者名称转换的动作。当 sendmail 收到一个要送给 xxx 的信时,它会依据 aliases档的内容送给另一个使用者。这个功能可以创造一个只有在信件系统内才有效的使用者。例如 mailing list 就会用到这个功能,在 mailinglist 中,我们可能会创造一个叫 redlinux@link.ece.uci.edu 的 mailinglist,但实际上并没有一个叫 redlinux 的使用者。实际 aliases 档的内容是将送给这个使用者的信都收给 mailing list 处理程式负责分送的工作。

  /etc/aliases 是一个文字模式的档案,sendmail 需要一个二进位格式的 /etc/aliases.db。newaliases 的功能传是将 /etc/aliases 转换成一个 sendmail 所能了解的资料库。

  参数:没有任何参数。 范例:
  # newaliases

  下面命令会做相同的事,
  # sendmail -bi
  相关命令:
  mail, mailq, newaliases, sendmail

  名称:talk
  使用权限:所有使用者
  使用方式:
  talk person [ttyname]
  说明:与其他使用者对谈
  把计:
  person:预备对谈的使用者帐号,如果该使用者在其他机器上,则可输入 person@machine.name
  ttyname:如果使用者同时有两个以上的 tty 连线,可以自行选择合适的 tty 传讯息
  例子.1:
  与现在机器上的使用者Rollaend对谈,此时 Rollaend 只有一个连线:
  talk Rollaend
  接下来就是等Rollaend回应,若Rollaend接受,则Rollaend输入 `talk jzlee`即可开始对谈,结束请按 ctrl+c
  例子.2 :与linuxfab.cx上的使用者Rollaend对谈,使用pts/2来对谈:
  talk Rollaend@linuxfab.cx pts/2

  接下来就是等Rollaend回应,若Rollaend接受,则Rollaend输入 `talk jzlee@jzlee.home`即可开始对谈,结束请按 ctrl+c
  注意:若萤幕的字会出现不正常的字元,试着按 ctrl+l 更新萤幕画面。

  名称:wall
  使用权限:所有使用者
  使用方式:
  wall [ message ]
  使用说明:
  wall 会将讯息传给每一个 mesg 设定为 yes 的上线使用者。当使用终端机介面做为标准传入时, 讯息结束时需加上 EOF (通常用 Ctrl+D)
  例子:
  传讯息"hi" 给每一个使用者:
  wall hi

  名称:write
  使用权限:所有使用者
  使用方式:
  write user [ttyname]
  说明:传讯息给其他使用者
  把计:
  user:预备传讯息的使用者帐号
  ttyname:如果使用者同时有两个以上的 tty 连线,可以自行选择合适的 tty 传讯息
  例子.1:
  传讯息给 Rollaend,此时 Rollaend 只有一个连线:
  write Rollaend


  接下来就是将讯息打上去,结束请按 ctrl+c

  例子.2 :传讯息给 Rollaend,Rollaend 的连线有 pts/2,pts/3:
  write Rollaend pts/2

  接下来就是将讯息打上去,结束请按 ctrl+c
  注意:若对方设定 mesg n,则此时讯席将无法传给对方

  名称:kill
  使用权限:所有使用者
  使用方式:
  kill [ -s signal | -p ] [ -a ] pid ...
  kill -l [ signal ]
  说明:kill 送出一个特定的信号 (signal) 给行程 id 为 pid 的行程根据该信号而做特定的动作, 若没有指定, 预设是送出终止 (TERM) 的信号
  把计:
  -s (signal):其中可用的讯号有 HUP (1), KILL (9), TERM (15), 分别代表着重跑, 砍掉, 结束; 详细的信号可以用 kill -l
  -p:印出 pid , 并不送出信号
  -l (signal):列出所有可用的信号名称
  范例:
  将 pid 为 323 的行程砍掉 (kill):
  kill -9 323
  将 pid 为 456 的行程重跑 (restart):
  kill -HUP 456


  名称:nice
  使用权限:所有使用者

  使用方式:nice [-n adjustment] [-adjustment] [--adjustment=adjustment] [--help] [--version] [command [arg...]]
  说明:以更改过的优先序来执行程式, 如果未指定程式, 则会印出目前的排程优先序, 内定的 adjustment 为 10, 范围为 -20 (最高优先序) 到 19 (最低优先序)
  把计:
  -n adjustment, -adjustment, --adjustment=adjustment 皆为将该原有优先序的增加 adjustment
  --help 显示求助讯息
  --version 显示版本资讯
  范例:
  将 ls 的优先序加 1 并执行:
  nice -n 1 ls

  将 ls 的优先序加 10 并执行:
  nice ls将 ls 的优先序加 10 并执行

  注意:优先序 (priority) 为作业系统用来决定 CPU 分配的参数,Linux 使用『回合制(round-robin)』的演算法来做 CPU 排程,优先序越高,所可能获得的 CPU时间就越多。

 

  名称:ps
  使用权限:所有使用者
  使用方式:ps [options] [--help]
  说明:显示瞬间行程 (process) 的动态
  参数:
  ps 的参数非常多, 在此仅列出几个常用的参数并大略介绍含义
  -A 列出所有的行程
  -w 显示加宽可以显示较多的资讯
  -au 显示较详细的资讯
  -aux 显示所有包含其他使用者的行程

  au(x) 输出格式:

  USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
  USER: 行程拥有者
  PID: pid
  %CPU: 占用的 CPU 使用率
  %MEM: 占用的记忆体使用率
  VSZ: 占用的虚拟记忆体大小
  RSS: 占用的记忆体大小
  TTY: 终端的次要装置号码 (minor device number of tty)
  STAT: 该行程的状态:
  D: 不可中断的静止 (通悸□□缜b进行 I/O 动作)
  R: 正在执行中
  S: 静止状态
  T: 暂停执行
  Z: 不存在但暂时无法消除
  W: 没有足够的记忆体分页可分配
  <: 高优先序的行程
  N: 低优先序的行程
  L: 有记忆体分页分配并锁在记忆体内 (即时系统或捱A I/O)
  START: 行程开始时间
  TIME: 执行的时间
  COMMAND:所执行的指令

  范例:

  ps
  PID TTY TIME CMD
  2791 ttyp0 00:00:00 tcsh
  3092 ttyp0 00:00:00 ps
  % ps -A
  PID TTY TIME CMD
  1 ? 00:00:03 init
  2 ? 00:00:00 kflushd
  3 ? 00:00:00 kpiod
  4 ? 00:00:00 kswapd
  5 ? 00:00:00 mdrecoveryd
  .......
  % ps -aux
  USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
  root 1 0.0 0.7 1096 472 ? S Sep10 0:03 init [3]
  root 2 0.0 0.0 0 0 ? SW Sep10 0:00 [kflushd]
  root 3 0.0 0.0 0 0 ? SW Sep10 0:00 [kpiod]
  root 4 0.0 0.0 0 0 ? SW Sep10 0:00 [kswapd]
  ........


  名称:pstree
  使用权限:所有使用者
  使用方式:
  pstree [-a] [-c] [-h|-Hpid] [-l] [-n] [-p] [-u] [-G|-U] [pid|user]
  pstree -V
  说明:将所有行程以树状图显示, 树状图将会以 pid (如果有指定) 或是以 init 这个基本行程为根 (root) ,如果有指定使用者 id , 则树状图会只显示该使用者所拥有的行程
  参数:
  -a 显示该行程的完整指令及参数, 如果是被记忆体置换出去的行程则会加上括号
  -c 如果有重覆的行程名, 则分开列出 (预设值是会在前面加上 *
  范例:

  pstree

  init-+-amd
  |-apmd
  |-atd
  |-httpd---10*[httpd]
  %pstree -p
  init(1)-+-amd(447)
  |-apmd(105)
  |-atd(339)
  %pstree -c
  init-+-amd
  |-apmd
  |-atd
  |-httpd-+-httpd
  | |-httpd
  | |-httpd
  | |-httpd
  ....

 

  名称:renice
  使用权限:所有使用者

  使用方式:renice priority [[-p] pid ...] [[-g] pgrp ...] [[-u] user ...]

  说明:重新指定一个或多个行程(Process)的优先序(一个或多个将根据所下的参数而定)
  把计:
  -p pid 重新指定行程的 id 为 pid 的行程的优先序
  -g pgrp 重新指定行程群组(process group)的 id 为 pgrp 的行程 (一个或多个) 的优先序
  -u user 重新指定行程拥有者为 user 的行程的优先序
  范例:
  将行程 id 为 987 及 32 的行程与行程拥有者为 daemon 及 root 的优先序号码加 1:
  renice +1 987 -u daemon root -p 32
  注意:每一个行程(Process)都有一个唯一的 (unique) id


  名称:top
  使用权限:所有使用者
  使用方式:top [-] [d delay] [q] [c] [S] [s] [i] [n] [b]
  说明:即时显示 process 的动态
  把计:
  d:改变显示的更新速度,或是在交谈式指令列( interactive command)按 s
  q:没有任何延迟的显示速度,如果使用者是有 superuser 的权限,则 top 将会以最高的优先序执行
  c:切换显示模式,共有两种模式,一是只显示执行档的名称,另一种是显示完整的路径与名称S:累积模式,会将己完成或消失的子行程 ( dead child process ) 的 CPU time 累积起来
  s:安全模式,将交谈式指令取消, 避免潜在的危机
  i:不显示任何闲置 (idle) 或无用 (zombie) 的行程
  n:更新的次数,完成后将会退出 top
  b:批次档模式,搭配 "n" 参数一起使用,可以用来将 top 的结果输出到档案内
  范例:
  显示更新十次后退出 ;
  top -n 10

  使用者将不能利用交谈式指令来对行程下命令:
  top -s

  将更新显示二次的结果输入到名称为 top.log 的档案里:
  top -n 2 -b < top.log

  名称:skill
  使用权限:所有使用者
  使用方式: skill [signal to send] [options] 选择程序的规则
  说明:

  送个讯号给正在执行的程序,预设的讯息为 TERM (中断) , 较常使用的讯息为 HUP , INT , KILL , STOP , CONT ,和 0
  讯息有三种写法:分别为 -9 , -SIGKILL , -KILL , 可以使用 -l 或 -L 已列出可使用的讯息。
  一般参数:
  -f 快速模式/尚未完成
  -i 互动模式/ 每个动作将要被确认
  -v 详细输出/ 列出所选择程序的资讯
  -w 智能警告讯息/ 尚未完成
  -n 没有动作/ 显示程序代号
  参数:选择程序的规则可以是, 终端机代号,使用者名称,程序代号,命令名称。
  -t 终端机代号 ( tty 或 pty )
  -u 使用者名称
  -p 程序代号 ( pid )
  -c 命令名称 可使用的讯号:
  以下列出已知的讯号名称,讯号代号,功能。

  名称 (代号) 功能/ 描述
  ALRM 14 离开
  HUP 1 离开
  INT 2 离开
  KILL 9 离开/ 强迫关闭
  PIPE 13 离开
  POLL 离开
  PROF 离开
  TERM 15 离开
  USR1 离开
  USR2 离开
  VTALRM 离开
  STKFLT 离开/ 只适用于i386, m68k, arm 和 ppc 硬体
  UNUSED 离开/ 只适用于i386, m68k, arm 和 ppc 硬体
  TSTP 停止 /产生与内容相关的行为
  TTIN 停止 /产生与内容相关的行为
  TTOU 停止 /产生与内容相关的行为
  STOP 停止 /强迫关闭
  CONT 从新启动 /如果在停止状态则从新启动,否则忽略
  PWR 忽略 /在某些系统中会离开
  WINCH 忽略
  CHLD 忽略
  ABRT 6 核心
  FPE 8 核心
  ILL 4 核心
  QUIT 3 核心
  SEGV 11 核心
  TRAP 5 核心
  SYS 核心 /或许尚未实作
  EMT 核心 /或许尚未实作
  BUS 核心 /核心失败
  XCPU 核心 /核心失败
  XFSZ 核心 /核心失败
  范例:
  停止所有在 PTY 装置上的程序
  skill -KILL -v pts/*
  停止三个使用者 user1 , user2 , user3
  skill -STOP user1 user2 user3

  其他相关的命令: kill

  名称:expr
  使用权限:所有使用者
  ### 字串长度
  shell>> expr length "this is a test"
  14

  ### 数字商数
  shell>> expr 14 % 9
  5

  ### 从位置处抓取字串
  shell>> expr substr "this is a test" 3 5
  is is

  ### 数字串 only the first character

  shell>> expr index "testforthegame" e
  2

  ### 字串真实重现
  shell>> expr quote thisisatestformela
  thisisatestformela


  名称: tr
  ### 1.比方说要把目录下所有的大写档名换为小写档名?

  似乎有很多方式,"tr"是其中一种:
  #!/bin/sh
  dir="/tmp/testdir";
  files=`find $dir -type f`;
  for i in $files
  do
  dir_name=`dirname $i`;
  ori_filename=`basename $i`
  new_filename=`echo $ori_filename | tr [:upper:] [:lower:]` > /dev/null;
  #echo $new_filename;
  mv $dir_name/$ori_filename $dir_name/$new_filename
  done

  ### 2.自己试验中...lowercase to uppercase

  tr abcdef...[del] ABCDE...[del]
  tr a-z A-Z
  tr [:lower:] [:upper:]

  shell>> echo "this is a test" | tr a-z A-Z > www
  shell>> cat www
  THIS IS A TEST

  ### 3.去掉不想要的字串
  shell>> tr -d this ### 去掉有关 t.e.s.t
  this

  man
  man
  test
  e
  ### 4.取代字串
  shell>> tr -s "this" "TEST"
  this
  TEST
  th
  TE

  指令:clear
  用途:清除萤幕用。
  使用方法:在 console 上输入 clear。

  名称: reset, tset
  使用方法: tset [-IQqrs] [-] [-e ch] [-i ch] [-k ch] [-m mapping] [terminal]
  使用说明:
  reset 其实和 tset 是一同个命令,它的用途是设定终端机的状态。一般而言,这个命令会自动的从环境变数,命令列或是其它的组态档决定目前终端机的型态。如果指定型态是 ? 的话,这个程式会要求使用者输入终端机的型别。

  由于这个程式会将终端机设回原始的状态,除了在 login 时使用外,当系统终端机因为程式不正常执行而进入一些奇怪的状态时,你也可以用它来重设终端机o 例如不小心把二进位档用 cat 指令进到终端机,常会有终端机不再回应键盘输入,或是回应一些奇怪字元的问题。此时就可以用 reset 将终端机回复至原始状态。选项说明:
  -p
  将终端机类别显示在萤幕上,但不做设定的动作。这个命令可以用来取得目前终端机的类别。
  -e ch
  将 erase 字元设成 ch
  -i ch
  将中断字元设成 ch
  -k ch
  将删除一行的字元设成 ch
  -I
  不要做设定的动作,如果没有使用选项 -Q 的话,erase,中断及删除字元的目前值依然会送到萤幕上。
  -Q
  不要显示 erase,中断及删除字元的值到萤幕上。
  -r
  将终端机类别印在萤幕上。
  -s
  将设定 TERM 用的命令用字串的型式送到终端机中,通常在 .login 或 .profile 中用
  范例:
  让使用者输入一个终端机型别并将终端机设到该型别的预设状态。
  # reset ?

  将 erase 字元设定 control-h
  # reset -e ^B

  将设定用的字串显示在萤幕上
  # reset -s
  Erase is control-B (^B).
  Kill is control-U (^U).
  Interrupt is control-C (^C).
  TERM=xterm;

  名称:compress
  使用权限:所有使用者
  使用方式:compress [-dfvcV] [-b maxbits] [file ...]
  说明:
  compress 是一个相当古老的 unix 档案压缩指令,压缩后的档案会加上一个 .Z 延伸档名以区别未压缩的档案,压缩后的档案可以以 uncompress 解压。若要将数个档案压成一个压缩档,必须先将档案 tar 起来再压缩。由于 gzip 可以产生更理想的压缩比例,一般人多已改用 gzip 为档案压缩工具。
  参数:
  c 输出结果至标准输出设备(一般指荧幕)
  f 强迫写入档案,若目的档已经存在,则会被覆盖 (force)
  v 将程式执行的讯息印在荧幕上 (verbose)
  b 设定共同字串数的上限,以位元计算,可以设定的值为 9 至 16 bits 。由于值越大,能使用的共同字串就 越多,压缩比例就越大,所以一般使用预设值 16 bits (bits)
  d 将压缩档解压缩
  V 列出版本讯息
  范例:
  将 source.dat 压缩成 source.dat.Z ,若 source.dat.Z 已经存在,内容则会被压缩档覆盖。
  compress -f source.dat

  将 source.dat 压缩成 source.dat.Z ,并列印出压缩比例。
  -v 与 -f 可以一起使用
  compress -vf source.dat

  将压缩后的资料输出后再导入 target.dat.Z 可以改变压缩档名。
  compress -c source.dat > target.dat.Z

  -b 的值越大,压缩比例就越大,范围是 9-16 ,预设值是 16 。
  compress -b 12 source.dat

  将 source.dat.Z 解压成 source.dat ,若档案已经存在,使用者按 y 以确定覆盖档案,若使用 -df 程式则会自动覆盖档案。由于系统会自动加入 .Z 为延伸档名,所以 source.dat 会自动当作 source.dat.Z 处理。

  compress -d source.dat
  compress -d source.dat.Z

  名称: lpd
  使用权限: 所有使用者
  使用方式:lpd [-l] [#port]
  lpd 是一个常驻的印表机管理程式,它会根据 /etc/printcap 的内容来管理本地或远端的印表机。/etc/printcap 中定义的每一个印表机必须在 /var/lpd 中有一个相对应的目录,目录中以 cf 开头的档案表示一个等待送到适当装置的印表工作。这个档案通常是由 lpr 所产生。

  lpr 和 lpd 组成了一个可以离线工作的系统,当你使用 lpr 时,印表机不需要能立即可用,甚至不用存在。lpd 会自动监视印表机的状况,当印表机上线后,便立即将档案送交处理。这个得所有的应用程式不必等待印表机完成前一工作。
  参数:
  -l: 将一些除错讯息显示在标准输出上。
  #port: 一般而言,lpd 会使用 getservbyname 取得适当的 TCP/IP port,你可以使用这个参数强迫 lpd 使用指定的 port。
  范例:
  这个程式通常是由 /etc/rc.d 中的程式在系统启始阶段执行。

  名称 lpq
  -- 显示列表机贮列中未完成的工作 用法
  lpq [l] [P] [user]
  说明
  lpq 会显示由 lpd 所管理的列表机贮列中未完成的项目。
  范例
  范例 1. 显示所有在 lp 列表机贮列中的工作
  # lpq -PlpRank Owner Job Files Total Size1st root 238 (standard input) 1428646 bytes

  相关函数
  lpr,lpc,lpd

  名称: lpr
  使用权限: 所有使用者
  使用方式:lpr [ -P printer ]
  将档案或是由标准输入送进来的资料送到印表机贮列之中,印表机管理程式 lpd 会在稍后将这个档案送给适当的程式或装置处理。lpr 可以用来将料资送给本地或是远端的主机来处理。参数:
  -p Printer: 将资料送至指定的印表机 Printer,预设值为 lp。
  范例:
  将 www.c 和 kkk.c 送到印表机 lp。
  lpr -Plp www.c kkk.c

  名称: lprm
  -- 将一个工作由印表机贮列中移除 用法
  /usr/bin/lprm [P] [file...]
  说明
  尚未完成的印表机工作会被放在印表机贮列之中,这个命令可用来将常未送到印表机的工作取消。由于每一个印表机都有一个独立的贮列,你可以用 -P 这个命令设定想要作用的印列机。如果没有设定的话,会使用系统预设的印表机。
  这个命令会检查使用者是否有足够的权限删除指定的档案,一般而言,只有档案的拥有者或是系统管理员才有这个权限。
  范例
  将印表机 hpprinter 中的第 1123 号工作移除
  lprm -Phpprinter 1123

  将第 1011 号工作由预设印表机中移除
  lprm 1011

  名称: fdformat
  使用权限: 所有使用者
  使用方式:fdformat [-n] device
  使用说明:
  对指定的软碟机装置进行低阶格式化。使用这个指令对软碟格式化的时候,最好指定像是下面的装置:

  /dev/fd0d360 磁碟机 A: ,磁片为 360KB 磁碟
  /dev/fd0h1440 磁碟机 A: ,磁片为 1.4MB 磁碟
  /dev/fd1h1200 磁碟机 B: ,磁片为 1.2MB 磁碟
  如果使用像是 /dev/fd0 之类的装置,如果里面的磁碟不是标准容量,格式化可能会失败。在这种情况之下,使用者可以用 setfdprm 指令先行指定必要参数。
  参数:
  -n 关闭确认功能。这个选项会关闭格式化之后的确认步骤。
  范例:
  fdformat -n /dev/fd0h1440
  将磁碟机 A 的磁片格式化成 1.4MB 的磁片。并且省略确认的步骤。

  名称: mformat
  使用权限: 所有使用者
  使用方式:
  mformat [-t cylinders] [-h heads] [-s sectors] [-l volume_label] [-F] [-I fsVer-sion] [-S sizecode] [-2 sectors_on_track_0] [-M software_sector_size] [-a] [-X] [-C] [-H hidden_sectors] [-r root_sectors] [-B boot_sector] [-0 rate_on_track_0] [-A rate_on_other_tracks] [-1] [-k] drive:

  在已经做过低阶格式化的磁片上建立 DOS 档案系统。如果在编译 mtools 的时候把 USE_2M 的参数打开,部分与 2M 格式相关的参数就会发生作用。否则这些参数(像是 S,2,1,M)不会发生作用。
  参数:
  -t 磁柱(synlider)数
  -h 磁头(head)数
  -s 每一磁轨的磁区数
  -l 标签
  -F 将磁碟格式化为 FAT32 格式,不过这个参数还在实验中。
  -I 设定 FAT32 中的版本号。这当然也还在实验中。
  -S 磁区大小代码,计算方式为 sector = 2^(大小代码+7)
  -c 磁丛(cluster)的磁区数。如果所给定的数字会导致磁丛数超过 FAT 表的限制,mformat 会自动放大磁区数。
  -s
  -M 软体磁区大小。这个数字就是系统回报的磁区大小。通常是和实际的大小相同。
  -a 如果加上这个参数,mformat 会产生一组 Atari 系统的序号给这块软碟。
  -X 将软碟格式化成 XDF 格式。使用前必须先用 xdfcopy 指令对软碟作低阶格式化的动作。
  -C 产生一个可以安装 MS-DOS 档案系统的磁碟影像档(disk image)。当然对一个实体磁碟机下这个参数是没有意义的。
  -H 隐藏磁区的数目。这通常适用在格式化硬碟的分割区时,因为通常一个分割区的前面还有分割表。这个参数未经测试,能不用就不用。
  -n 磁碟序号
  -r 根目录的大小,单位是磁区数。这个参数只对 FAT12 和 FAT16 有效。
  -B 使用所指定的档案或是设备的开机磁区做为这片磁片或分割区的开机磁区。当然当中的硬体参数会随之更动。
  -k 尽量保持原有的开机磁区。
  -0 第 0 轨的资料传输率
  -A 第 0 轨以外的资料传输率
  -2 使用 2m 格式
  -1 不使用 2m 格式
  范例:
  mformat a:
  这样会用预设值把 a: (就是 /dev/fd0)里的磁碟片格式化。

  名称: mkdosfs
  使用权限: 所有使用者
  使用方式: mkdosfs [ -c | -l filename ]
  [ -f number_of_FATs ]
  [ -F FAT_size ]
  [ -i volume_id ]
  [ -m message_file ]
  [ -n volume_name ]
  [ -r root_dir_entry ]
  [ -s sector_per_cluster ]
  [ -v ]
  device
  [ block_count ]
  说明: 建立 DOS 档案系统。 device 指你想要建立 DOS 档案系统的装置代号。像是 /dev/hda1 等等。 block_count 则是你希望配置的区块数。如果 block_count 没有指定则系统会自动替你计算符合该装置大小的区块数。
  参数:
  -c 建立档案系统之前先检查是否有坏轨。
  -l 从得定的档案中读取坏轨记录。
  -f 指定档案配置表(FAT , File Allocation Table)的数量。预设值为 2 。目前 Linux 的 FAT 档案系统不支援超过 2 个 FAT 表。通常这个不需要改。
  -F 指定 FAT 表的大小,通常是 12 或是 16 个位元组。12 位元组通常用于磁碟片,16 位元组用于一般硬碟的分割区,也就是所谓的 FAT16 格式。这个值通常系统会自己选定适当的值。在磁碟片上用 FAT16 通常不会发生作用,反之在硬碟上用 FAT12 亦然。
  -i 指定 Volume ID。一般是一个 4 个位元组的数字,像是 2e203a47 。如果不给系统会自己产生。
  -m 当使用者试图用这片磁片或是分割区开机,而上面没有作业系统时,系统会给使用者一段警告讯息。这个参数就是用来变更这个讯息的。你可以先用档案编辑好,然后用这个参数指定,或是用
  -m -
  这样系统会要求你直接输入这段文字。要特别注意的是,档案里的字串长度不要超过 418 个字,包括展开的跳栏符号(TAB)和换行符号(换行符号在 DOS 底下算两个字元!)
  -n 指定 Volume Name,就是磁碟标签。如同在 DOS 底下的 format 指令一样,给不给都可以。没有预设值。
  -r 指定根目录底下的最大档案数。这里所谓的档案数包括目录。预设值是在软碟上是 112 或是 224 ,在硬碟上是 512。没事不要改这个数字。
  -s 每一个磁丛(cluster)的磁区数。必须是 2 的次方数。不过除非你知道你在作什么,这个值不要乱给。
  -v 提供额外的讯息
  范例:
  mkdosfs -n Tester /dev/fd0 将 A 槽里的磁碟片格式化为 DOS 格式,并将标签设为 Tester

posted @ 2006-03-31 11:55 高山流水 阅读(84) | 评论 (0)编辑 收藏


目录:
一、术语session
二、HTTP协议与状态保持
三、理解cookie机制
四、理解session机制
五、理解javax.servlet.http.HttpSession
六、HttpSession常见问题
七、跨应用程序的session共享
八、总结
参考文档

一、术语session
在我的经验里,session这个词被滥用的程度大概仅次于transaction,更加有趣的是transaction与session在某些语境下的含义是相同的。

session,中文经常翻译为会话,其本来的含义是指有始有终的一系列动作/消息,比如打电话时从拿起电话拨号到挂断电话这中间的一系列过程可以称之为一个 session。有时候我们可以看到这样的话“在一个浏览器会话期间,...”,这里的会话一词用的就是其本义,是指从一个浏览器窗口打开到关闭这个期间 ①。最混乱的是“用户(客户端)在一次会话期间”这样一句话,它可能指用户的一系列动作(一般情况下是同某个具体目的相关的一系列动作,比如从登录到选购商品到结账登出这样一个网上购物的过程,有时候也被称为一个transaction),然而有时候也可能仅仅是指一次连接,也有可能是指含义①,其中的差别只能靠上下文来推断②。

然而当session一词与网络协议相关联时,它又往往隐含了“面向连接”和/或“保持状态”这样两个含义, “面向连接”指的是在通信双方在通信之前要先建立一个通信的渠道,比如打电话,直到对方接了电话通信才能开始,与此相对的是写信,在你把信发出去的时候你并不能确认对方的地址是否正确,通信渠道不一定能建立,但对发信人来说,通信已经开始了。“保持状态”则是指通信的一方能够把一系列的消息关联起来,使得消息之间可以互相依赖,比如一个服务员能够认出再次光临的老顾客并且记得上次这个顾客还欠店里一块钱。这一类的例子有“一个TCP session”或者 “一个POP3 session”③。

而到了web服务器蓬勃发展的时代,session在web开发语境下的语义又有了新的扩展,它的含义是指一类用来在客户端与服务器之间保持状态的解决方案④。有时候session也用来指这种解决方案的存储结构,如“把xxx保存在session 里”⑤。由于各种用于web开发的语言在一定程度上都提供了对这种解决方案的支持,所以在某种特定语言的语境下,session也被用来指代该语言的解决方案,比如经常把Java里提供的javax.servlet.http.HttpSession简称为session⑥。

鉴于这种混乱已不可改变,本文中session一词的运用也会根据上下文有不同的含义,请大家注意分辨。
在本文中,使用中文“浏览器会话期间”来表达含义①,使用“session机制”来表达含义④,使用“session”表达含义⑤,使用具体的“HttpSession”来表达含义⑥

二、HTTP协议与状态保持
HTTP 协议本身是无状态的,这与HTTP协议本来的目的是相符的,客户端只需要简单的向服务器请求下载某些文件,无论是客户端还是服务器都没有必要纪录彼此过去的行为,每一次请求之间都是独立的,好比一个顾客和一个自动售货机或者一个普通的(非会员制)大卖场之间的关系一样。

然而聪明(或者贪心?)的人们很快发现如果能够提供一些按需生成的动态信息会使web变得更加有用,就像给有线电视加上点播功能一样。这种需求一方面迫使HTML逐步添加了表单、脚本、DOM等客户端行为,另一方面在服务器端则出现了CGI规范以响应客户端的动态请求,作为传输载体的HTTP协议也添加了文件上载、 cookie这些特性。其中cookie的作用就是为了解决HTTP协议无状态的缺陷所作出的努力。至于后来出现的session机制则是又一种在客户端与服务器之间保持状态的解决方案。

让我们用几个例子来描述一下cookie和session机制之间的区别与联系。笔者曾经常去的一家咖啡店有喝5杯咖啡免费赠一杯咖啡的优惠,然而一次性消费5杯咖啡的机会微乎其微,这时就需要某种方式来纪录某位顾客的消费数量。想象一下其实也无外乎下面的几种方案:
1、该店的店员很厉害,能记住每位顾客的消费数量,只要顾客一走进咖啡店,店员就知道该怎么对待了。这种做法就是协议本身支持状态。
2、发给顾客一张卡片,上面记录着消费的数量,一般还有个有效期限。每次消费时,如果顾客出示这张卡片,则此次消费就会与以前或以后的消费相联系起来。这种做法就是在客户端保持状态。
3、发给顾客一张会员卡,除了卡号之外什么信息也不纪录,每次消费时,如果顾客出示该卡片,则店员在店里的纪录本上找到这个卡号对应的纪录添加一些消费信息。这种做法就是在服务器端保持状态。

由于HTTP协议是无状态的,而出于种种考虑也不希望使之成为有状态的,因此,后面两种方案就成为现实的选择。具体来说cookie机制采用的是在客户端保持状态的方案,而session机制采用的是在服务器端保持状态的方案。同时我们也看到,由于采用服务器端保持状态的方案在客户端也需要保存一个标识,所以session机制可能需要借助于cookie机制来达到保存标识的目的,但实际上它还有其他选择。

三、理解cookie机制 
cookie机制的基本原理就如上面的例子一样简单,但是还有几个问题需要解决:“会员卡”如何分发;“会员卡”的内容;以及客户如何使用“会员卡”。

正统的cookie分发是通过扩展HTTP协议来实现的,服务器通过在HTTP的响应头中加上一行特殊的指示以提示浏览器按照指示生成相应的cookie。然而纯粹的客户端脚本如JavaScript或者VBScript也可以生成cookie。

而cookie 的使用是由浏览器按照一定的原则在后台自动发送给服务器的。浏览器检查所有存储的cookie,如果某个cookie所声明的作用范围大于等于将要请求的资源所在的位置,则把该cookie附在请求资源的HTTP请求头上发送给服务器。意思是麦当劳的会员卡只能在麦当劳的店里出示,如果某家分店还发行了自己的会员卡,那么进这家店的时候除了要出示麦当劳的会员卡,还要出示这家店的会员卡。

cookie的内容主要包括:名字,值,过期时间,路径和域。
其中域可以指定某一个域比如.google.com,相当于总店招牌,比如宝洁公司,也可以指定一个域下的具体某台机器比如www.google.com或者froogle.google.com,可以用飘柔来做比。
路径就是跟在域名后面的URL路径,比如/或者/foo等等,可以用某飘柔专柜做比。
路径与域合在一起就构成了cookie的作用范围。
如果不设置过期时间,则表示这个cookie的生命期为浏览器会话期间,只要关闭浏览器窗口,cookie就消失了。这种生命期为浏览器会话期的 cookie被称为会话cookie。会话cookie一般不存储在硬盘上而是保存在内存里,当然这种行为并不是规范规定的。如果设置了过期时间,浏览器就会把cookie保存到硬盘上,关闭后再次打开浏览器,这些cookie仍然有效直到超过设定的过期时间。

存储在硬盘上的cookie 可以在不同的浏览器进程间共享,比如两个IE窗口。而对于保存在内存里的cookie,不同的浏览器有不同的处理方式。对于IE,在一个打开的窗口上按 Ctrl-N(或者从文件菜单)打开的窗口可以与原窗口共享,而使用其他方式新开的IE进程则不能共享已经打开的窗口的内存cookie;对于 Mozilla Firefox0.8,所有的进程和标签页都可以共享同样的cookie。一般来说是用javascript的window.open打开的窗口会与原窗口共享内存cookie。浏览器对于会话cookie的这种只认cookie不认人的处理方式经常给采用session机制的web应用程序开发者造成很大的困扰。

下面就是一个goolge设置cookie的响应头的例子
HTTP/1.1 302 Found
Location: http://www.google.com/intl/zh-CN/
Set-Cookie: PREF=ID=0565f77e132de138:NW=1:TM=1098082649:LM=1098082649:S=KaeaCFPo49RiA_d8; expires=Sun, 17-Jan-2038 19:14:07 GMT; path=/; domain=.google.com
Content-Type: text/html




这是使用HTTPLook这个HTTP Sniffer软件来俘获的HTTP通讯纪录的一部分




浏览器在再次访问goolge的资源时自动向外发送cookie

 


使用Firefox可以很容易的观察现有的cookie的值
使用HTTPLook配合Firefox可以很容易的理解cookie的工作原理。




IE也可以设置在接受cookie前询问

 


这是一个询问接受cookie的对话框。

四、理解session机制
session机制是一种服务器端的机制,服务器使用一种类似于散列表的结构(也可能就是使用散列表)来保存信息。

当程序需要为某个客户端的请求创建一个session的时候,服务器首先检查这个客户端的请求里是否已包含了一个session标识 - 称为 session id,如果已包含一个session id则说明以前已经为此客户端创建过session,服务器就按照session id把这个 session检索出来使用(如果检索不到,可能会新建一个),如果客户端请求不包含session id,则为此客户端创建一个session并且生成一个与此session相关联的session id,session id的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,这个 session id将被在本次响应中返回给客户端保存。

保存这个session id的方式可以采用cookie,这样在交互过程中浏览器可以自动的按照规则把这个标识发挥给服务器。一般这个cookie的名字都是类似于SEEESIONID,而。比如weblogic对于web应用程序生成的cookie,JSESSIONID= ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764,它的名字就是 JSESSIONID。

由于cookie可以被人为的禁止,必须有其他机制以便在cookie被禁止时仍然能够把session id传递回服务器。经常被使用的一种技术叫做URL重写,就是把session id直接附加在URL路径的后面,附加方式也有两种,一种是作为URL路径的附加信息,表现形式为http://...../xxx;jsessionid= ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764
另一种是作为查询字符串附加在URL后面,表现形式为http://...../xxx?jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764
这两种方式对于用户来说是没有区别的,只是服务器在解析的时候处理的方式不同,采用第一种方式也有利于把session id的信息和正常程序参数区分开来。
为了在整个交互过程中始终保持状态,就必须在每个客户端可能请求的路径后面都包含这个session id。

另一种技术叫做表单隐藏字段。就是服务器会自动修改表单,添加一个隐藏字段,以便在表单提交时能够把session id传递回服务器。比如下面的表单
<form name="testform" action="/xxx">
<input type="text">
</form>
在被传递给客户端之前将被改写成
<form name="testform" action="/xxx">
<input type="hidden" name="jsessionid" value="ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764">
<input type="text">
</form>
这种技术现在已较少应用,笔者接触过的很古老的iPlanet6(SunONE应用服务器的前身)就使用了这种技术。
实际上这种技术可以简单的用对action应用URL重写来代替。

在谈论session机制的时候,常常听到这样一种误解“只要关闭浏览器,session就消失了”。其实可以想象一下会员卡的例子,除非顾客主动对店家提出销卡,否则店家绝对不会轻易删除顾客的资料。对session来说也是一样的,除非程序通知服务器删除一个session,否则服务器会一直保留,程序一般都是在用户做log off的时候发个指令去删除session。然而浏览器从来不会主动在关闭之前通知服务器它将要关闭,因此服务器根本不会有机会知道浏览器已经关闭,之所以会有这种错觉,是大部分session机制都使用会话cookie来保存session id,而关闭浏览器后这个 session id就消失了,再次连接服务器时也就无法找到原来的session。如果服务器设置的cookie被保存到硬盘上,或者使用某种手段改写浏览器发出的HTTP请求头,把原来的session id发送给服务器,则再次打开浏览器仍然能够找到原来的session。

恰恰是由于关闭浏览器不会导致session被删除,迫使服务器为seesion设置了一个失效时间,当距离客户端上一次使用session的时间超过这个失效时间时,服务器就可以认为客户端已经停止了活动,才会把session删除以节省存储空间。

五、理解javax.servlet.http.HttpSession
HttpSession是Java平台对session机制的实现规范,因为它仅仅是个接口,具体到每个web应用服务器的提供商,除了对规范支持之外,仍然会有一些规范里没有规定的细微差异。这里我们以BEA的Weblogic Server8.1作为例子来演示。

首先,Weblogic Server提供了一系列的参数来控制它的HttpSession的实现,包括使用cookie的开关选项,使用URL重写的开关选项,session持久化的设置,session失效时间的设置,以及针对cookie的各种设置,比如设置cookie的名字、路径、域, cookie的生存时间等。

一般情况下,session都是存储在内存里,当服务器进程被停止或者重启的时候,内存里的session也会被清空,如果设置了session的持久化特性,服务器就会把session保存到硬盘上,当服务器进程重新启动或这些信息将能够被再次使用, Weblogic Server支持的持久性方式包括文件、数据库、客户端cookie保存和复制。

复制严格说来不算持久化保存,因为session实际上还是保存在内存里,不过同样的信息被复制到各个cluster内的服务器进程中,这样即使某个服务器进程停止工作也仍然可以从其他进程中取得session。

cookie生存时间的设置则会影响浏览器生成的cookie是否是一个会话cookie。默认是使用会话cookie。有兴趣的可以用它来试验我们在第四节里提到的那个误解。

cookie的路径对于web应用程序来说是一个非常重要的选项,Weblogic Server对这个选项的默认处理方式使得它与其他服务器有明显的区别。后面我们会专题讨论。

关于session的设置参考[5] http://e-docs.bea.com/wls/docs70/webapp/weblogic_xml.html#1036869

六、HttpSession常见问题
(在本小节中session的含义为⑤和⑥的混合)


1、session在何时被创建
一个常见的误解是以为session在有客户端访问时就被创建,然而事实是直到某server端程序调用 HttpServletRequest.getSession(true)这样的语句时才被创建,注意如果JSP没有显示的使用 <% @page session="false"%> 关闭session,则JSP文件在编译成Servlet时将会自动加上这样一条语句 HttpSession session = HttpServletRequest.getSession(true);这也是JSP中隐含的 session对象的来历。

由于session会消耗内存资源,因此,如果不打算使用session,应该在所有的JSP中关闭它。

2、session何时被删除
综合前面的讨论,session在下列情况下被删除a.程序调用HttpSession.invalidate();或b.距离上一次收到客户端发送的session id时间间隔超过了session的超时设置;或c.服务器进程被停止(非持久session)

3、如何做到在浏览器关闭时删除session
严格的讲,做不到这一点。可以做一点努力的办法是在所有的客户端页面里使用javascript代码window.oncolose来监视浏览器的关闭动作,然后向服务器发送一个请求来删除session。但是对于浏览器崩溃或者强行杀死进程这些非常规手段仍然无能为力。

4、有个HttpSessionListener是怎么回事
你可以创建这样的listener去监控session的创建和销毁事件,使得在发生这样的事件时你可以做一些相应的工作。注意是session的创建和销毁动作触发listener,而不是相反。类似的与HttpSession有关的listener还有 HttpSessionBindingListener,HttpSessionActivationListener和 HttpSessionAttributeListener。

5、存放在session中的对象必须是可序列化的吗
不是必需的。要求对象可序列化只是为了session能够在集群中被复制或者能够持久保存或者在必要时server能够暂时把session交换出内存。在 Weblogic Server的session中放置一个不可序列化的对象在控制台上会收到一个警告。我所用过的某个iPlanet版本如果 session中有不可序列化的对象,在session销毁时会有一个Exception,很奇怪。

6、如何才能正确的应付客户端禁止cookie的可能性
对所有的URL使用URL重写,包括超链接,form的action,和重定向的URL,具体做法参见[6]
http://e-docs.bea.com/wls/docs70/webapp/sessions.html#100770

7、开两个浏览器窗口访问应用程序会使用同一个session还是不同的session
参见第三小节对cookie的讨论,对session来说是只认id不认人,因此不同的浏览器,不同的窗口打开方式以及不同的cookie存储方式都会对这个问题的答案有影响。

8、如何防止用户打开两个浏览器窗口操作导致的session混乱
这个问题与防止表单多次提交是类似的,可以通过设置客户端的令牌来解决。就是在服务器每次生成一个不同的id返回给客户端,同时保存在session里,客户端提交表单时必须把这个id也返回服务器,程序首先比较返回的id与保存在session里的值是否一致,如果不一致则说明本次操作已经被提交过了。可以参看《J2EE核心模式》关于表示层模式的部分。需要注意的是对于使用javascript window.open打开的窗口,一般不设置这个id,或者使用单独的id,以防主窗口无法操作,建议不要再window.open打开的窗口里做修改操作,这样就可以不用设置。

9、为什么在Weblogic Server中改变session的值后要重新调用一次session.setValue
做这个动作主要是为了在集群环境中提示Weblogic Server session中的值发生了改变,需要向其他服务器进程复制新的session值。

10、为什么session不见了
排除session正常失效的因素之外,服务器本身的可能性应该是微乎其微的,虽然笔者在iPlanet6SP1加若干补丁的Solaris版本上倒也遇到过;浏览器插件的可能性次之,笔者也遇到过3721插件造成的问题;理论上防火墙或者代理服务器在cookie处理上也有可能会出现问题。
出现这一问题的大部分原因都是程序的错误,最常见的就是在一个应用程序中去访问另外一个应用程序。我们在下一节讨论这个问题。

七、跨应用程序的session共享

常常有这样的情况,一个大项目被分割成若干小项目开发,为了能够互不干扰,要求每个小项目作为一个单独的web应用程序开发,可是到了最后突然发现某几个小项目之间需要共享一些信息,或者想使用session来实现SSO(single sign on),在session中保存login的用户信息,最自然的要求是应用程序间能够访问彼此的session。

然而按照Servlet规范,session的作用范围应该仅仅限于当前应用程序下,不同的应用程序之间是不能够互相访问对方的session的。各个应用服务器从实际效果上都遵守了这一规范,但是实现的细节却可能各有不同,因此解决跨应用程序session共享的方法也各不相同。

首先来看一下Tomcat是如何实现web应用程序之间session的隔离的,从 Tomcat设置的cookie路径来看,它对不同的应用程序设置的cookie路径是不同的,这样不同的应用程序所用的session id是不同的,因此即使在同一个浏览器窗口里访问不同的应用程序,发送给服务器的session id也可以是不同的。


  

根据这个特性,我们可以推测Tomcat中session的内存结构大致如下。


 

笔者以前用过的iPlanet也采用的是同样的方式,估计SunONE与iPlanet之间不会有太大的差别。对于这种方式的服务器,解决的思路很简单,实际实行起来也不难。要么让所有的应用程序共享一个session id,要么让应用程序能够获得其他应用程序的session id。

iPlanet中有一种很简单的方法来实现共享一个session id,那就是把各个应用程序的cookie路径都设为/(实际上应该是/NASApp,对于应用程序来讲它的作用相当于根)。
<session-info>
<path>/NASApp</path>
</session-info>

需要注意的是,操作共享的session应该遵循一些编程约定,比如在session attribute名字的前面加上应用程序的前缀,使得 setAttribute("name", "neo")变成setAttribute("app1.name", "neo"),以防止命名空间冲突,导致互相覆盖。


在Tomcat中则没有这么方便的选择。在Tomcat版本3上,我们还可以有一些手段来共享session。对于版本4以上的Tomcat,目前笔者尚未发现简单的办法。只能借助于第三方的力量,比如使用文件、数据库、JMS或者客户端cookie,URL参数或者隐藏字段等手段。

我们再看一下Weblogic Server是如何处理session的。


  

从截屏画面上可以看到Weblogic Server对所有的应用程序设置的cookie的路径都是/,这是不是意味着在Weblogic Server中默认的就可以共享session了呢?然而一个小实验即可证明即使不同的应用程序使用的是同一个session,各个应用程序仍然只能访问自己所设置的那些属性。这说明Weblogic Server中的session的内存结构可能如下


 

对于这样一种结构,在 session机制本身上来解决session共享的问题应该是不可能的了。除了借助于第三方的力量,比如使用文件、数据库、JMS或者客户端 cookie,URL参数或者隐藏字段等手段,还有一种较为方便的做法,就是把一个应用程序的session放到ServletContext中,这样另外一个应用程序就可以从ServletContext中取得前一个应用程序的引用。示例代码如下,

应用程序A
context.setAttribute("appA", session); 

应用程序B
contextA = context.getContext("/appA");
HttpSession sessionA = (HttpSession)contextA.getAttribute("appA"); 

值得注意的是这种用法不可移植,因为根据ServletContext的JavaDoc,应用服务器可以处于安全的原因对于context.getContext("/appA");返回空值,以上做法在Weblogic Server 8.1中通过。

那么Weblogic Server为什么要把所有的应用程序的cookie路径都设为/呢?原来是为了SSO,凡是共享这个session的应用程序都可以共享认证的信息。一个简单的实验就可以证明这一点,修改首先登录的那个应用程序的描述符weblogic.xml,把cookie路径修改为/appA 访问另外一个应用程序会重新要求登录,即使是反过来,先访问cookie路径为/的应用程序,再访问修改过路径的这个,虽然不再提示登录,但是登录的用户信息也会丢失。注意做这个实验时认证方式应该使用FORM,因为浏览器和web服务器对basic认证方式有其他的处理方式,第二次请求的认证不是通过 session来实现的。具体请参看[7] secion 14.8 Authorization,你可以修改所附的示例程序来做这些试验。

八、总结
session机制本身并不复杂,然而其实现和配置上的灵活性却使得具体情况复杂多变。这也要求我们不能把仅仅某一次的经验或者某一个浏览器,服务器的经验当作普遍适用的经验,而是始终需要具体情况具体分析。

关于作者:
郎云鹏(dev2dev ID: hippiewolf),软件工程师,从事J2EE开发
电子邮件:langyunpeng@yahoo.com.cn
地址:大连软件园路31号科技大厦A座大连博涵咨询服务有限公司

参考文档:
[1] Preliminary Specification http://wp.netscape.com/newsref/std/cookie_spec.html
[2] RFC2109 http://www.rfc-editor.org/rfc/rfc2109.txt
[3] RFC2965 http://www.rfc-editor.org/rfc/rfc2965.txt
[4] The Unofficial Cookie FAQ http://www.cookiecentral.com/faq/
[5] http://e-docs.bea.com/wls/docs70/webapp/weblogic_xml.html#1036869
[6] http://e-docs.bea.com/wls/docs70/webapp/sessions.html#100770
[7] RFC2616 http://www.rfc-editor.org/rfc/rfc2616.txt

posted @ 2006-03-31 11:54 高山流水 阅读(100) | 评论 (0)编辑 收藏

 


级别: 中级

Chris Grindstaff
软件工程师, IBM
25 日 5 月 2004 年

静态分析工具承诺无需开发人员费劲就能找出代码中已有的缺陷。当然,如果有多年的编写经验,就会知道这些承诺并不是一定能兑现。尽管如此,好的静态分析工具仍然是工具箱中的无价之宝。在这个由两部分组成的系列文章的第一部分中,高级软件工程师 Chris Grindstaff 分析了 FindBugs 如何帮助提高代码质量以及排除隐含的缺陷。

代码质量工具的一个问题是它们容易为开发人员提供大量但并非真正问题的问题——即 伪问题(false positives)。出现伪问题时,开发人员要学会忽略工具的输出或者放弃它。FindBugs 的设计者 David Hovemeyer 和 William Pugh 注意到了这个问题,并努力减少他们所报告的伪问题数量。与其他静态分析工具不同,FindBugs 不注重样式或者格式,它试图只寻找真正的缺陷或者潜在的性能问题。

FindBugs 是什么?
FindBugs 是一个静态分析工具,它检查类或者 JAR 文件,将字节码与一组缺陷模式进行对比以发现可能的问题。有了静态分析工具,就可以在不实际运行程序的情况对软件进行分析。不是通过分析类文件的形式或结构来确定程序的意图,而是通常使用 Visitor 模式(请参阅 参考资料)。图 1 显示了分析一个匿名项目的结果(为防止可怕的犯罪,这里不给出它的名字):

图 1. FindBugs UI
Configure Detector 的图形表示

让我们看几个 FindBugs 可以发现的问题。

本系列的第二篇文章“编写自定义检测器”解释了如何编写自定义检测器, 以便发现特定于应用程序的问题。

问题发现的例子
下面的列表没有包括 FindBug 可以找到的 所有 问题。相反,我侧重于一些更有意思的问题。

检测器:找出 hash equals 不匹配
这个检测器寻找与 equals()hashCode() 的实现相关的几个问题。这两个方法非常重要,因为几乎所有基于集合的类—— List、Map、Set 等都调用它们。一般来说,这个检测器寻找两种不同类型的问题——当一个类:

  • 重写对象的 equals() 方法,但是没有重写它的 hashCode 方法,或者相反的情况时。

  • 定义一个 co-variant 版本的 equals()compareTo() 方法。例如,Bob 类定义其 equals() 方法为布尔 equals(Bob),它覆盖了对象中定义的 equals() 方法。因为 Java 代码在编译时解析重载方法的方式,在运行时使用的几乎总是在对象中定义的这个版本的方法,而不是在 Bob 中定义的那一个(除非显式将 equals() 方法的参数强制转换为 Bob 类型)。因此,当这个类的一个实例放入到类集合中的任何一个中时,使用的是 Object.equals() 版本的方法,而不是在 Bob 中定义的版本。在这种情况下,Bob 类应当定义一个接受类型为 Object 的参数的 equals() 方法。

检测器:忽略方法返回值
这个检测器查找代码中忽略了不应该忽略的方法返回值的地方。这种情况的一个常见例子是在调用 String 方法时,如在清单 1 中:

清单 1. 忽略返回值的例子
														
1  String aString = "bob";
2  b.replace('b', 'p');
3  if(b.equals("pop"))

												

这个错误很常见。在第 2 行,程序员认为他已经用 p 替换了字符串中的所有 b。确实是这样,但是他忘记了字符串是不可变的。所有这类方法都返回一个新字符串,而从来不会改变消息的接收者。

检测器:Null 指针对 null 的解引用(dereference)和冗余比较
这个检测器查找两类问题。它查找代码路径将会或者可能造成 null 指针异常的情况,它还查找对 null 的冗余比较的情况。例如,如果两个比较值都为 null,那么它们就是冗余的并可能表明代码错误。FindBugs 在可以确定一个值为 null 而另一个值不为 null 时,检测类似的错误,如清单 2 所示:

清单 2. Null 指针示例
														
1  Person person = aMap.get("bob");
2  if (person != null) {
3      person.updateAccessTime();
4  }
5  String name = person.getName();

												

在这个例子中,如果第 1 行的 Map 不包括一个名为“bob”的人,那么在第 5 行询问 person 的名字时就会出现 null 指针异常。因为 FindBugs 不知道 map 是否包含“bob”,所以它将第 5 行标记为可能 null 指针异常。

检测器:初始化之前读取字段
这个检测器寻找在构造函数中初始化之前被读取的字段。这个错误通常是——尽管不总是如此——由使用字段名而不是构造函数参数引起的,如清单 3 所示:

清单 3. 在构造函数中读取未初始化的字段
														
1  public class Thing {
2      private List actions;
3      public Thing(String startingActions) {
4          StringTokenizer tokenizer = new StringTokenizer(startingActions);
5          while (tokenizer.hasMoreTokens()) {
6              actions.add(tokenizer.nextToken());
7          }   
8      }
9  }

												

在这个例子中,第 6 行将产生一个 null 指针异常,因为变量 actions 还没有初始化。

这些例子只是 FindBugs 所发现的问题种类的一小部分(更多信息请参阅 参考资料)。在撰写本文时,FindBugs 提供总共 35 个检测器。

开始使用 FindBugs
要运行 FindBugs,需要一个版本 1.4 或者更高的 Java Development Kit (JDK),尽管它可以分析由老的 JDK 创建的类文件。要做的第一件事是下载并安装最新发布的 FindBugs——当前是 0.7.1 (请参阅 参考资料)。幸运的是,下载和安全是相当简单的。在下载了 zip 或者 tar 文件后,将它解压缩到所选的目录中。就是这样了——安装就完成了。

安装完后,对一个示例类运行它。就像一般文章中的情况,我将针对 Windows 用户进行讲解,并假定那些 Unix 信仰者可以熟练地转化这些内容并跟进。打开命令行提示符号并进入 FindBugs 的安装目录。对我来说,这是 C:\apps\FindBugs-0.7.3。

在 FindBugs 主目录中,有几个值得注意的目录。文档在 doc 目录中,但是对我们来说更重要的是,bin 目录包含了运行 FindBugs 的批处理文件,这使我们进入下一部分。

运行 FindBugs
像如今的大多数数工具一样,可以以多种方式运行 FindBugs——从 GUI、从命令行、使用 Ant、作为 Eclipse 插件程序和使用 Maven。我将简要提及从 GUI 运行 FindBugs,但是重点放在用 Ant 和命令行运行它。部分原因是由于 GUI 没有提供命令行的所有选项。例如,当前不能指定要加入的过滤器或者在 UI 中排除特定的类。但是更重要的原因是我认为 FindBugs 最好作为编译的集成部分使用,而 UI 不属于自动编译。

使用 FindBugs UI
使用 FindBugs UI 很直观,但是有几点值得说明。如 图 1 所示,使用 FindBugs UI 的一个好处是对每一个检测到的问题提供了说明。图 1 显示了缺陷 Naked notify in method 的说明。对每一种缺陷模式提供了类似的说明,在第一次熟悉这种工具时这是很有用的。窗口下面的 Source code 选项卡也同样有用。如果告诉 FindBugs 在什么地方寻找代码,它就会在转换到相应的选项卡时突出显示有问题的那一行。

值得一提的还有在将 FinBugs 作为 Ant 任务或者在命令行中运行 FindBugs 时,选择 xml 作为 ouput 选项,可以将上一次运行的结果装载到 UI 中。这样做是同时利用基于命令行的工具和 UI 工具的优点的一个很好的方法。

将 FindBugs 作为 Ant 任务运行
让我们看一下如何在 Ant 编译脚本中使用 FindBugs。首先将 FindBugs Ant 任务拷贝到 Ant 的 lib 目录中,这样 Ant 就知道新的任务。将 FIND_BUGS_HOME\lib\FindBugs-ant.jar 拷贝到 ANT_HOME\lib。

现在看看在编译脚本中要加入什么才能使用 FindBugs 任务。因为 FindBugs 是一个自定义任务,将需要使用 taskdef 任务以使 Ant 知道装载哪一个类。通过在编译文件中加入以下一行做到这一点:

														
<taskdef name="FindBugs" classname="edu.umd.cs.FindBugs.anttask.FindBugsTask"/>

												

在定义了 taskdef 后,可以用它的名字 FindBugs 引用它。下一步要在编译中加入使用新任务的目标,如清单 4 所示:

清单 4. 创建 FindBugs 目录
														
1  <target name="FindBugs" depends="compile">
2      <FindBugs home="${FindBugs.home}" output="xml" outputFile="jedit-output.xml">
3          <class location="c:\apps\JEdit4.1\jedit.jar" />
4          <auxClasspath path="${basedir}/lib/Regex.jar" />
5          <sourcePath path="c:\tempcbg\jedit" />
6      </FindBugs>
7  </target>

												

让我们更详细地分析这段代码中所发生的过程。

第 1 行: 注意 target 取决于编译。一定要记住处理的是类文件而 是源文件,这样使 target 对应于编译目标保证了 FindBugs 可在最新的类文件运行。FindBugs 可以灵活地接受多种输入,包括一组类文件、JAR 文件、或者一组目录。

第 2 行: 必须指定包含 FindBugs 的目录,我是用 Ant 的一个属性完成的,像这样:

														
<property name="FindBugs.home" value="C:\apps\FindBugs-0.7.3" />

												

可选属性 output 指定 FindBugs 的结果使用的输出格式。可能的值有 xmltext 或者 emacs。如果没有指定 outputFile,那么 FindBugs 会使用标准输出。如前所述,XML 格式有可以在 UI 中观看的额外好处。

第 3 行: class 元素用于指定要 FindBugs 分析哪些 JAR、类文件或者目录。分析多个 JAR 或者类文件时,要为每一个文件指定一个单独的 class 元素。除非加入了 projectFile 元素,否则需要 class 元素。更多细节请参阅 FindBugs 手册。

第 4 行: 用嵌套元素 auxClasspath 列出应用程序的依赖性。这些是应用程序需要但是不希望 FindBugs 分析的类。如果没有列出应用程序的依赖关系,那么 FindBugs 仍然会尽可能地分析类,但是在找不到一个缺少的类时,它会抱怨。与 class 元素一样,可以在 FindBugs 元素中指定多个 auxClasspath 元素。auxClasspath 元素是可选的。

第 5 行: 如果指定了 sourcePath 元素,那么 path 属性应当表明一个包含应用程序源代码的目录。指定目录使 FindBugs 可以在 GUI 中查看 XML 结果时突出显示出错的源代码。这个元素是可选的。

上面就是基本内容了。让我们提前几个星期。

过滤器
您已经将 FindBugs 引入到了团队中,并运行它作为您的每小时/每晚编译过程的一部分。当团队越来越熟悉这个工具时,出于某些原因,您决定所检测到的一些缺陷对于团队来说不重要。也许您不关心一些类是否返回可能被恶意修改的对象——也许,像 JEdit,有一个真正需要的(honest-to-goodness)、合法的理由调用 System.gc()

总是可以选择“关闭”特定的检测器。在更细化的水平上,可以在指定的一组类甚至是方法中查找问题时,排除某些检测器。FindBugs 提供了这种细化的控制,可以排除或者包含过滤器。当前只有用命令行或者 Ant 启动的 FindBugs 中支持排除和包含过滤器。正如其名字所表明的,使用排除过滤器来排除对某些缺陷的报告。较为少见但仍然有用的是,包含过滤器只能用于报告指定的缺陷。过滤器是在一个 XML 文件中定义的。可以在命令行中用一个排除或者包含开关、或者在 Ant 编译文件中用 excludeFilterincludeFilter 指定它们。在下面的例子中,假定使用排除开关。还要注意在下面的讨论中,我对 “bugcode”、“bug” 和“detector”的使用具有某种程度的互换性。

可以有不同的方式定义过滤器:

  • 匹配一个类的过滤器。可以用这些过滤器 忽略在特定类中发现的所有问题。

  • 匹配一个类中特定缺陷代码(bugcode)的 过滤器。可以用这些过滤器忽略在特定类中发现的一些缺陷。

  • 匹配一组缺陷的过滤器。可以用这些过滤器 忽略所分析的所有类中的一组缺陷。

  • 匹配所分析的一个类中的某些方法的过滤器。可以用这些过滤器忽略在一个类中的一组方法中发现的所有缺陷。

  • 匹配在所分析的一个类中的方法中发现的某些缺陷的过滤器。可以用这些过滤器忽略在一组方法中发现的特定缺陷。

知道了这些就可以开始使用了。有关其他定制 FindBugs 方法的更多信息,请参阅 FindBugs 文档。知道如何设置编译文件以后,就让我们更详细地分析如何将 FindBugs 集成到编译过程中吧!

将 FindBugs 集成到编译过程中
在将 FindBugs 集成到编译过程当中可以有几种选择。总是可以在命令行执行 FindBugs,但是您很可能已经使用 Ant 进行编译,所以最自然的方法是使用 FindBugs Ant 任务。因为我们在 如何运行 FindBugs 一节中讨论了使用 FindBugs Ant 任务的基本内容,所以现在讨论应当将 FindBugs 加入到编译过程中的几个理由,并讨论几个可能遇到的问题。

为什么应该将 FindBugs 集成到编译过程中?
经常问到的第一个问题是为什么要将 FindBugs 加入到编译过程中?虽然有大量理由,最明显的回答是要保证尽可能早地在进行编译时发现问题。当团队扩大,并且不可避免地在项目中加入更多新开发人员时,FindBugs 可以作为一个安全网,检测出已经识别的缺陷模式。我想重申在一篇 FindBugs 论文中表述的一些观点。如果让一定数量的开发人员共同工作,那么在代码中就会出现缺陷。像 FindBugs 这样的工具当然不会找出所有的缺陷,但是它们会帮助找出其中的部分。现在找出部分比客户在以后找到它们要好——特别是当将 FindBugs 结合到编译过程中的成本是如此低时。

一旦确定了加入哪些过滤器和类,运行 FindBugs 就没什么成本了,而带来的好处就是它会检测出新缺陷。如果编写特定于应用程序的检测器,则这个好处可能更大。

生成有意义的结果
重要的是要认识到这种成本/效益分析只有在不生成大量误检时才有效。换句话说,如果在每次编译时,不能简单地确定是否引入了新的缺陷,那么这个工具的价值就会被抵消。分析越自动化越好。如果修复缺陷意味着必须吃力地分析检测出的大量不相干的缺陷,那么您就不会经常使用它,或者至少不会很好地使用它。

确定不关心哪些问题并从编译中排除它们。也可以挑出 确实 关注的一小部分检测器并只运行它们。另一种选择是从个别的类中排除一组检测器,但是其他的类不排除。FindBugs 提供了使用过滤器的极大灵活性,这可帮助生成对团队有意义的结果,由此我们进入下一节。

确定用 FindBugs 的结果做什么
可能看来很显然,但是您想不到我参与的团队中有多少加入了类似 FindBugs 这样的工具而没有真正利用它。让我们更深入地探讨这个问题——用结果做什么?明确回答这个问题是困难的,因为这与团队的组织方式、如何处理代码所有权问题等有很大关系。不过,下面是一些指导:

  • 可以考虑将 FindBugs 结果加入到源代码管理(SCM)系统中。一般的经验做法是不将编译工件(artifact)放到 SCM 系统中。不过,在这种特定情况下,打破这个规则可能是正确的,因为它使您可以监视代码质量随时间的变化。

  • 可以选择将 XML 结果转换为可以发送到团队的网站上的 HTML 报告。转换可以用 XSL 样式表或者脚本实现。有关例子请查看 FindBugs 网站或者邮件列表(请参阅 参考资料)。

  • 像 FindBugs 这样的工具通常会成为用于敲打团队或者个人的政治武器。尽量抵制这种做法或者不让它发生——记住,它只是一个工具,它可以帮助改进代码的质量。有了这种思想,在下一部分中,我将展示如何编写自定义缺陷检测器。

结束语
我鼓励读者对自己的代码试用静态分析工具,不管是 FindBugs、PMD 还是其他的。它们是有用的工具,可以找出真正的问题,而 FindBugs 是在消除误检方面做得最好的工具。此外,它的可插入结构提供了编写有价值的、特定于应用程序的检测器的、有意思的测试框架。在本系列的 第 2 部分 中,我将展示如何编写自定义检测器以找出特定于应用程序的问题。

参考资料

关于作者
Chris Grindstaff 是在北加利福尼亚 Research Triangle Park 工作的 IBM 高级软件工程师。Chris 在 7 岁时编写了他的第一个程序,当时他让小学老师认识到“键入”句子与手写它们一样费力。Chris 目前参与了不同的开放源代码项目。他大量使用 Eclipse 并编写了几个流行的 Eclipse 插件程序,可以在他的 网站 找到这些插件程序。可以通过 cgrinds@us.ibm.com 或者 chris@gstaff.org 与 Chrise 联系。


转自IBM.CN

posted @ 2006-03-31 11:53 高山流水 阅读(5288) | 评论 (8)编辑 收藏

1、大多数人想要改造这个世界,但却罕有人想改造自己。
2、积极的人在每一次忧患中都看到一个机会, 而消极的人则在每个机会都看到某种忧患。
3、莫找借口失败,只找理由成功。(不为失败找理由,要为成功找方法)
4、伟人之所以伟大,是因为他与别人共处逆境时,别人失去了信心,他却下决心实现自己的目标。
5、世上没有绝望的处境,只有对处境绝望的人。
6、当你感到悲哀痛苦时,最好是去学些什么东西。学习会使你永远立于不败之地。
7、世界上那些最容易的事情中,拖延时间最不费力。
8、人之所以能,是相信能。
9、一个有信念者所开发出的力量,大于99个只有兴趣者。
10、每一发奋努力的背后,必有加倍的赏赐。   
11、人生伟业的建立 ,不在能知,乃在能行。
12、任何的限制,都是从自己的内心开始的。
13、含泪播种的人一定能含笑收获。
14、欲望以提升热忱,毅力以磨平高山。
15、一个能从别人的观念来看事情,能了解别人心灵活动的人永远不必为自己的前途担心。
16、一个人最大的破产是绝望,最大的资产是希望。
17、不要等待机会,而要创造机会。
18、如果寒暄只是打个招呼就了事的话,那与猴子的呼叫声有什么不同呢? 事实上,正确的寒暄必须在短短一句话中明显地表露出你对他的关怀。
19、昨晚多几分钟的准备,今天少几小时的麻烦。
20、做对的事情比把事情做对重要。
21、人格的完善是本,财富的确立是末。
22、没有一种不通过蔑视、忍受和奋斗就可以征服的命运。
23、行动是治愈恐惧的良药,而犹豫、拖延将不断滋养恐惧。
24、没有天生的信心,只有不断培养的信心。
25、只有一条路不能选择——那就是放弃的路;只有一条路不能拒绝——那就是成长的路。
26、人性最可怜的就是:我们总是梦想着天边的一座奇妙的玫瑰园,而不去欣赏今天就开在我们窗口的玫瑰。
27、征服畏惧、建立自信的最快最确实的方法,就是去做你害怕的事,直到你获得成功的经验。
28、失败是什么?没有什么,只是更走近成功一步;成功是什么?就是走过了所有通向失败的路,只剩下一条路,那就是成功的路。
29、让我们将事前的忧虑,换为事前的思考和计划吧!
30、再长的路,一步步也能走完,再短的路,不迈开双脚也无法到达。
31、任何业绩的质变都来自于量变的积累。
32、成功不是将来才有的,而是从决定去做的那一刻起,持续累积而成。

posted @ 2006-03-31 11:52 高山流水 阅读(163) | 评论 (0)编辑 收藏

十八个故事

1\
    曾经有个小国到中国来,进贡了三个一模一样的金人,金碧辉煌,把皇帝高兴坏了。可是这小国不厚道,同时出一道题目:这三个金人哪个最有价值?
  皇帝想了许多的办法,请来珠宝匠检查,称重量,看做工,都是一模一样的。怎么办?使者还等着回去汇报呢。泱泱大国,不会连这个小事都不懂吧?
  最后,有一位退位的老大臣说他有办法。
  皇帝将使者请到大殿,老臣胸有成竹地拿着三根稻草,插入第一个金人的耳朵里,这稻草从另一边耳朵出来了。第二个金人的稻草从嘴巴里直接掉出来,而第三个金人,稻草进去后掉进了肚子,什么响动也没有。老臣说:第三个金人最有价值!使者默默无语,答案正确。
  这个故事告诉我们,最有价值的人,不一定是最能说的人的人。老天给我们两只耳朵一个嘴巴,本来就是让我们多听少说的。善于倾听,才是成熟的人最基本的素质。
2\ 
    陈阿土是台湾的农民,从来没有出过远门。攒了半辈子的钱,终于参加一个旅游团出了国。
  国外的一切都是非常新鲜的,关键是,陈阿土参加的是豪华团,一个人住一个标准间。这让他新奇不已。
  早晨,服务生来敲门送早餐时大声说道:“GOODMORNING SIR!”
  陈阿土愣住了。这是什么意思呢?在自己的家乡,一般陌生的人见面都会问:“您贵姓?”
  于是陈阿土大声叫道:“我叫陈阿土!”
  如是这般,连着三天,都是那个服务生来敲门,每天都大声说:“GOODMORNING SIR!”而陈阿土亦大声回道:“我叫陈阿土!”
  但他非常的生气。这个服务生也太笨了,天天问自己叫什么,告诉他又记不住,很烦的。终于他忍不住去问导游,“GOODMORNING SIR!”是什么意思,导游告诉了他,天啊!!真是丢脸死了。
  陈阿土反复练习“GOODMORNING SIR!”这个词,以便能体面地应对服务生。
  又一天的早晨,服务生照常来敲门,门一开陈阿土就大声叫道:“GOODMORNING SIR!”
  与此同时,服务生叫的是:“我是陈阿土!”
  这个故事告诉我们,人与人交往,常常是意志力与意志力的较量。不是你影响他,就是他影响你,而我们要想成功,一定要培养自己的影响力,只有影响力大的人才可以成为最强者。
3\
  有三个人要被关进监狱三年,监狱长给他们三个一人一个要求。
  美国人爱抽雪茄,要了三箱雪茄。
  法国人最浪漫,要一个美丽的女子相伴。
  而犹太人说,他要一部与外界沟通的电话。
  三年过后,第一个冲出来的是美国人,嘴里鼻孔里塞满了雪茄,大喊道:“给我火,给我火!”原来他忘了要火了。
  接着出来的是法国人。只见他手里抱着一个小孩子,美丽女子手里牵着一个小孩子,肚子里还怀着第三个。
  最后出来的是犹太人,他紧紧握住监狱长的手说:“这三年来我每天与外界联系,我的生意不但没有停顿,反而增长了200%,为了表示感谢,我送你一辆劳施莱斯!”
  这个故事告诉我们,什么样的选择决定什么样的生活。今天的生活是由三年前我们的选择决定的,而今天我们的抉择将决定我们三年后的生活。我们要选择接触最新的信息,了解最新的趋势,从而更好的创造自己的将来。
4\
    去过庙的人都知道,一进庙门,首先是弥陀佛,笑脸迎客,而在他的北面,则是黑口黑脸的韦陀。但相传在很久以前,他们并不在同一个庙里,而是分别掌管不同的庙。
弥乐佛热情快乐,所以来的人非常多,但他什么都不在乎,丢三拉四,没有好好的管理账务,所以依然入不敷出。而韦陀虽然管账是一把好手,但成天阴着个脸,太过严肃,搞得人越来越少,最后香火断绝。
佛祖在查香火的时候发现了这个问题,就将他们俩放在同一个庙里,由弥乐佛负责公关,笑迎八方客,于是香火大旺。而韦陀铁面无私, 锱珠必较,则让他负责财务,严格把关。在两人的分工合作中,庙里一派欣欣向荣景象。
  其实在用人大师的眼里,没有废人,正如武功高手,不需名贵宝剑,摘花飞叶即可伤人,关键看如何运用。  
5\
  一个人去买鹦鹉,看到一只鹦鹉前标:此鹦鹉会两门语言,售价二百元。
  另一只鹦鹉前则标道:此鹦鹉会四门语言,售价四百元。
  该买哪只呢?两只都毛色光鲜,非常灵活可爱。这人转啊转,拿不定主意。
  结果突然发现一只老掉了牙的鹦鹉,毛色暗淡散乱,标价八百元。
  这人赶紧将老板叫来:这只鹦鹉是不是会说八门语言?
  店主说:不。
  这人奇怪了:那为什么又老又丑,又没有能力,会值这个数呢?
  店主回答:因为另外两只鹦鹉叫这只鹦鹉老板。
  这故事告诉我们,真正的领导人,不一定自己能力有多强,只要懂信任,懂放权,懂珍惜,就能团结比自己更强的力量,从而提升自己的身价。
  相反许多能力非常强的人却因为过于完美主义,事必躬亲,什么人都不如自己,最后只能做最好的攻关人员,销售代表,成不了优秀的领导人。
6\
  A,在合资公司做白领,觉得自己满腔抱负没有得到上级的赏识,经常想:如果有一天能见到老总,有机会展示一下自己的才干就好了!!
  A的同事B,也有同样的想法,他更进一步,去打听老总上下班的时间,算好他大概会在何时进电梯,他也在这个时候去坐电梯,希望能遇到老总,有机会可以打个招呼。
  他们的同事C更进一步。他详细了解老总的奋斗历程,弄清老总毕业的学校,人际风格,关心的问题,精心设计了几句简单却有份量的开场 白,在算好的时间去乘坐电梯,跟老总打过几次招呼后,终于有一天跟老总长谈了一次,不久就争取到了更好的职位。
  愚者错失机会,智者善抓机会,成功者创造机会。机会只给准备好的人,这准备二字,并非说说而已。
7\
  一个心理学教授到疯人院参观,了解疯子的生活状态。一天下来,觉得这些人疯疯癫癫,行事出人意料,可算大开眼界。
  想不到准备返回时,发现自己的车胎被人下掉了。“一定是哪个疯子干的!”教授这样愤愤地想道,动手拿备胎准备装上。
  事情严重了。下车胎的人居然将螺丝也都下掉。没有螺丝有备胎也上不去啊!
  教授一筹莫展。在他着急万分的时候,一个疯子蹦蹦跳跳地过来了,嘴里唱着不知名的欢乐歌曲。他发现了困境中的教授,停下来问发生了什么事。
  教授懒得理他,但出于礼貌还是告诉了他。
  疯子哈哈大笑说:“我有办法!”他从每个轮胎上面下了一个螺丝,这样就拿到三个螺丝将备胎装了上去。
  教授惊奇感激之余,大为好奇:“请问你是怎么想到这个办法的?”
  疯子嘻嘻哈哈地笑道:“我是疯子,可我不是呆子啊!”
  其实,世上有许多的人,由于他们发现了工作中的乐趣,总会表现出与常人不一样的狂热,让人难以理解。许多人在笑话他们是疯子的时候,别人说不定还在笑他呆子呢。
  做人呆呆,处事聪明,在中国尤其不失为一种上佳做人姿态。
8\
  有一个博士分到一家研究所,成为学历最高的一个人。
  有一天他到单位后面的小池塘去钓鱼,正好正副所长在他的一左一右,也在钓鱼。
  他只是微微点了点头,这两个本科生,有啥好聊的呢?
  不一会儿,正所长放下钓竿,伸伸懒腰,蹭蹭蹭从水面上如飞地走到对面上厕所。
  博士眼睛睁得都快掉下来了。水上飘?不会吧?这可是一个池塘啊。
  正所长上完厕所回来的时候,同样也是蹭蹭蹭地从水上飘回来了。
  怎么回事?博士生又不好去问,自己是博士生哪!
  过一阵,副所长也站起来,走几步,蹭蹭蹭地飘过水面上厕所。这下子博士更是差点昏倒:不会吧,到了一个江湖高手集中的地方?
  博士生也内急了。这个池塘两边有围墙,要到对面厕所非得绕十分钟的路,而回单位上又太远,怎么办?
  博士生也不愿意去问两位所长,憋了半天后,也起身往水里跨:我就不信本科生能过的水面,我博士生不能过。
  只听咚的一声,博士生栽到了水里。
  两位所长将他拉了出来,问他为什么要下水,他问:“为什么你们可以走过去呢?”
  两所长相视一笑:“这池塘里有两排木桩子,由于这两天下雨涨水正好在水面下。我们都知道这木桩的位置,所以可以踩着桩子过去。你怎么不问一声呢?”
  学历代表过去,只有学习力才能代表将来。尊重经验的人,才能少走弯路。一个好的团队,也应该是学习型的团队。
9\
  A对B说:“我要离开这个公司。我恨这个公司!”
  B建议道:“我举双手赞成你报复!!破公司一定要给它点颜色看看。不过你现在离开,还不是最好的时机。”
  A问:???
  B说:“如果你现在走,公司的损失并不大。你应该趁着在公司的机会,拼命去为自己拉一些客户,成为公司独挡一面的人物,然后带着这 些客户突然离开公司,公司才会受到重大损失,非常被动。”
  A觉得B说的非常在理。于是努力工作,事遂所愿,半年多的努力工作后,他有了许多的忠实客户。
  再见面时B问A:现在是时机了,要跳赶快行动哦!
  A淡然笑道:老总跟我长谈过,准备升我做总经理助理,我暂时没有离开的打算了。
  其实这也正是B的初衷。一个人的工作,永远只是为自己的简历。只有付出大于得到,让老板真正看到你的能力大于位置,才会给你更多的机会替他创造更多利润。
10\
  有一位表演大师上场前,他的弟子告诉他鞋带松了。大师点头致谢,蹲下来仔细系好。等到弟子转身后,又蹲下来将鞋带解松。
  有个旁观者看到了这一切,不解地问:“大师,您为什么又要将鞋带解松呢?”大师回答道:“因为我饰演的是一位劳累的旅者,长途跋涉让他的鞋事松开,可以通过这个细节表现他的劳累憔悴.”
  “那你为什么不直接告诉你的弟子呢?”
  “他能细心地发现我的鞋带松了,并且热心地告诉我,我一定要保护他这种热情的积极性,及时地给他鼓励,至于为什么要将鞋带解开,将来会有更多的机会教他表演,可以下一次再说啊。”
  人一个时间只能做一件事,懂抓重点,才是真正的人才。
11\
  有个富家子弟特别爱吃饺子,每天都要吃。但他又特别刁,只吃馅,两头的皮尖尖就丢到后面的小河里去。
  好景不长,在他十六岁那年,一把大火烧了他的全家,父母急怒中相继病逝。这下他身无分文,又不好意思要饭。邻居家大嫂非常好,每餐给他吃一碗面糊糊。他则发奋读书,三年后考取官位回来,一定要感谢邻居大嫂。
  大嫂对他讲:不要感谢我。我没有给你什么,都是我收集的当年你丢的饺子皮尖,晒干后装了好凡麻袋,本来是想备不时之需的。正好你有需要,就又还给你了。
  大官思考良久,良久。。。。
  有一个有名的三八理论:八小时睡觉,八小时工作,这个人人一样。人与人之间的不同,是在于业余时间怎么渡过。时间是最有情,也最无情的东西,每人拥有的都一样,非常公平。但拥有资源的人不一定成功,善用资源的人才会成功。白天图生存,晚上求发展,这是二十一世纪对人才的要求。
12\
  两个人在森林里,遇到了一只大老虎。A就赶紧从背后取下一双更轻便的运动鞋换上。   B急死了,骂道:“你干嘛呢,再换鞋也跑不过老虎啊!”
  A说:“我只要跑得比你快就好了。”
  二十一世纪,没有危机感是最大的危机。特别是入关在即,电信,银行,保险,甚至是公务员这些我们以为非常稳定和有保障的企业,也会面临许多的变数。当更多的老虎来临时,我们没有有准备好自己的跑鞋?
13\
  父子两住山上,每天都要赶牛车下山卖柴。老父较有经验,坐镇驾车,山路崎岖,弯道特多,儿子眼神较好,总是在要转弯时提醒道:“爹,转弯啦!”
  有一次父亲因病没有下山,儿子一人驾车。到了弯道,牛怎么也不肯转弯,儿子用尽各种方法,下车又推又拉,用青草诱之,牛一动不动。
  到底是怎么回事?儿子百思不得其解。最后只有一个办法了,他左右看看无人,贴近牛的耳朵大声叫道:“爹,转弯啦!”
  牛应声而动。
  牛用条件反射的方式活着,而人则以习惯生活。一个成功的人晓得如何培养好的习惯来代替坏的习惯,当好的习惯积累多了,自然会有一个好的人生。
14\
    五岁的汉克和爸爸妈妈哥哥一起到森林干活,突然间下起雨来,可是他们只带了一块雨披。
  爸爸将雨披给了妈妈,妈妈给了哥哥,哥哥又给了汉克。
  汉克问道:“为什么爸爸给了妈妈,妈妈给了哥哥,哥哥又给了我呢?”
  爸爸回答道:“因为爸爸比妈妈强大,妈妈比哥哥强大,哥哥又比你强大呀。我们都会保护比较弱小的人。”
  汉克左右看了看,跑过去将雨披撑开来挡在了一朵风雨中飘摇的娇弱小花上面。
  这个故事告诉我们,真正的强者不一定是多有力,或者多有钱,而是他对别人多有帮助。
  责任可以让我们将事做完整,爱可以让我们将事情做好。
15\
  有位秀才第三次进京赶考,住在一个经常住的店里。考试前两天他做了三个梦,第一个梦是梦到自己在墙上种白菜,第二个梦是下雨天,他戴了斗笠还打伞,第三个梦是梦到跟心爱的表妹脱光了衣服躺在一起,但是背靠着背。
  这三个梦似乎有些深意,秀才第二天就赶紧去找算命的解梦。算命的一听,连拍大腿说:“你还是回家吧。你想想,高墙上种菜不是白费劲吗?戴斗笠打雨伞不是多此一举吗?跟表妹都脱光了躺在一张床上了,却背靠背,不是没戏吗?”
  秀才一听,心灰意冷,回店收拾包袱准备回家。店老板非常奇怪,问:“不是明天才考试吗,今天你怎么就回乡了?”
  秀才如此这般说了一番,店老板乐了:“哟,我也会解梦的。我倒觉得,你这次一定要留下来。你想想,墙上种菜不是高种吗?戴斗笠打伞不是说明你这次有备无患吗?跟你表妹脱光了背靠靠躺在床上,不是说明你翻身的时候就要到了吗?”
  秀才一听,更有道理,于是精神振奋地参加考试,居然中了个探花。
  积极的人,象太阳,照到哪里哪里亮,消极的人,象月亮,初一十五不一样。想法决定我们的生活,有什么样的想法,就有什么样的未来。
16\
  在动物园里的小骆驼问妈妈:“妈妈妈妈,为什么我们的睫毛那么地长?”
  骆驼妈妈说:“当风沙来的时候,长长的睫毛可以让我们在风暴中都能看得到方向。”
  小骆驼又问:“妈妈妈妈,为什么我们的背那么驼,丑死了!”
  骆驼妈妈说:“这个叫驼峰,可以帮我们储存大量的水和养分,让我们能在沙漠里耐受十几天的无水无食条件。”
  小骆驼又问:“妈妈妈妈,为什么我们的脚掌那么厚?”
  骆驼妈妈说:“那可以让我们重重的身子不至于陷在软软的沙子里,便于长途跋涉啊。”
  小骆驼高兴坏了:“哗,原来我们这么有用啊!!可是妈妈,为什么我们还在动物园里,不去沙漠远足呢?”
  天生我才必有用,可惜现在没人用。一个好的心态+一本成功的教材+一个无限的舞台=成功。每人的潜能是无限的,关键是要找到一个能充分发挥潜能的舞台。
17\
  有七个人曾经住在一起,每天分一大桶粥。要命的是,粥每天都是不够的。
  一开始,他们抓阄决定谁来分粥,每天轮一个。于是乎每周下来,他们只有一天是饱的,就是自己分粥的那一天。
  后来他们开始推选出一个道德高尚的人出来分粥。强权就会产生腐败,大家开始挖空心思去讨好他,贿赂他,搞得整个小团体乌烟障气。
  然后大家开始组成三人的分粥委员会及四人的评选委员会,互相攻击扯皮下来,粥吃到嘴里全是凉的。
  最后想出来一个方法:轮流分粥,但分粥的人要等其它人都挑完后拿剩下的最后一碗。为了不让自己吃到最少的,每人都尽量分得平均,就算不平,也只能认了。大家快快乐乐,和和气气,日子越过越好。
  同样是七个人,不同的分配制度,就会有不同的风气。所以一个单位如果有不好的工作习气,一定是机制问题,一定是没有完全公平公正公开,没有严格的奖勤罚懒。如何制订这样一个制度,是每个领导需要考虑的问题。
18\
  我想跟什么样的人合作
  曾经有人采访比尔盖次成功的秘决。比尔盖次说:因为又有更多的成功人士在为我工作。
  陈安之的超级成功学也有提到:先为成功的人工作,再与成功的人合作,最后是让成功的人为你工作。
  成功的人很多,但在我生活中我不认识,也没有办法去为他工作,而让成功的人为我工作,在现阶段,我更没有这个实力。
  只有合作,是我最喜欢和最欣赏的。我也力图借助一个宽松的环境和积极的团队,与更多的人公平合作,以便在未来替自己经营一个抵抗风险的事业。我最喜欢合作的人应该有以下几个特点:
  一。不甘心。二十一世纪,最大的危机是没有危机感,最大的陷阱是满足。人要学会用望远镜看世界,而不是用近视眼看世界。顺境时要想着为自己找个退路,逆境时要懂为自己找出路.
  二.学习力强.学历代表过去,学习力掌握将来.懂得从任何的细节,所有的人身上学习和感悟,并且要懂得举一反三。主要的是,学习,其实是学与习两个字。学一次,做一百次,才能真正掌握。学,做,教是一个完整的过程,只有达到教的程度,才算真正吃透。而且在更多时候,学习是一种态度。只有谦卑的人,才真正学到东西。大海之所以成为大海,是因为它比所有的河流都低。
  三。行动力强。只有行动才会有结果。行动不一样,结果才不一样。知道不去做,等于不知道,做了没有结果,等于没有做。不犯错误,一定会错,因为不犯错误的人一定没有尝试。错了不要紧,一定要善于总结,然后再做,一直到正确的结果出来为止。
  四。要懂付出。要想杰出一定得先付出。斤斤计较的人,一生只得两斤。没有点奉献精神,是不可能创业的。要先用行动让别人知道,你有超过所得的价值,别人才会开更高的价。
  五。有强烈的沟通意识。沟通无极限,这更是一种态度,而非一种技巧。一个好的团队当然要有共同的愿景,非一日可以得来。需要无时不在的沟通,从目标到细节,甚至到家庭等等,都在沟通的内容之列。
  六。诚恳大方。每人都有不同的立场,不可能要求利益都一致。关键是大家都要开诚布公地谈清楚,不要委屈求全。相信诚信才是合作的最好基石。
  七。有最基本的道德观。曾经有一个记者在家写稿时,他的四岁儿子吵着要他陪。记者很烦,就将一本杂志的封底撕碎,对他儿子说:“ 你先将这上面的世界地图拼完整,爸爸就陪你玩。”过了不到五分钟,儿子又来拖他的手说:“爸爸我拼好了,陪我玩!”
  记者很生气:“小孩子要玩是可以理解的,但如果说谎话就不好了。怎么可能这么快就拼好世界地图!”
  儿子非常委屈:“可是我真的拼好了呀!”
  记者一看,果然如此:不会吧?家里出现了神童?他非常好奇地问:“你是怎么做到的?”
  儿子说:世界地图的背面是一个人的头像。我反过来拼,只要这个人好了,世界就完整了。
  所以做事先做人。做人做好了,他的世界也就是好的。

posted @ 2006-03-31 11:51 高山流水 阅读(110) | 评论 (0)编辑 收藏

仅列出标题
共30页: First 11 12 13 14 15 16 17 18 19 Last