Skip to content Skip to main navigation Skip to footer

Linux

Linux:通过Ionic构建一个简单的混合式(Hybrid)跨平台移动应用

Linux:通过Ionic构建一个简单的混合式(Hybrid)跨平台移动应用
Linux:通过Ionic构建一个简单的混合式(Hybrid)跨平台移动应用

介绍

自从混合式移动开发火起来之后,一部分Web工程师开始转战移动开发。混合式移动开发技术让Web工程师可以开发出各个平台的移动应用,而且不需要 学习各个平台的原生编程语言。现在已经有很多诸如PhoneGap和Titanium这些混合式开发平台来帮助我们进行混合式编程,今天我们要介绍的是一个相比之下更新的混合式移动开发平台Ionic

Ionic是一个高级HTML5混合式移动应用开发框架,同时也是一个开源的前端框架。Ionic应用是基于Cordova的, 所以Cordova相关的工具也都可以构建到应用中去,Lonic注重的是视觉效果和用户体验,所以使用了 AngularJS来构建很各种酷的效果。

安装

想要开始Ionic开发,你需要先安装 Node.js

然后根据你的开发环境来安装相应的 Android 或 IOS 平台,在这篇文章中,我们会创建一个Android应用。

接下来你要安装一个 Cordova 和 Ionic的命令行工具,操作如下:

    npm install -g cordova ionic

 安装完成之后,你可以尝试开始创建一个工程:

    npm install -g cordova ionic

  进入项目目录,添加ionic平台,创建应用,在虚拟机中运行,成为高富帅……

    cd myIonicApp
    ionic platform add android
    ionic build android
    ionic emulate android

  下面就是样例应用的效果:

Linux:通过Ionic构建一个简单的混合式(Hybrid)跨平台移动应用
Linux:通过Ionic构建一个简单的混合式(Hybrid)跨平台移动应用

开始

我们已经有一个不错的开始了,现在我们来创建一个ToDo列表的应用,我们从空白模板开始:

ionic start myToDoList blank

  如果你进入到项目目录,你会看到AngularJS文件,这是我们添加相关代码的地方。

创建和展示列表

首先,你需要在应用中添加一个list,我们直接用 ion-list ,添加ion-list到www/index.html:

    
    Scuba Diving
    Climb Mount Everest
    

  之后我们看一看添加ion-list之后我们的html文件是什么样的:

    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    My ToDo List
    
    
    
    Scuba Diving
    Climb Mount Everest
    
    
    
    
    

 然后我们可以在 www/js/中创建一个controllers.js文件,来定义一个新的cntroller,我们暂且叫它ToDoListCtrl。这是controllers.js文件的内容:

    angular.module('starter.controllers',[])
    .controller('ToDoListCtrl',function($scope){
    });

  在上面的代码中,我们定义了一个叫starter的module和一个叫作calledToDoListCtrl的Controler。

然后我们就要把这个module加到我们的应用中了。打开www/js/app.js ,然后把module添加进去:

    angular.module('starter',['ionic','starter.controllers'])
    .run(function($ionicPlatform){
    $ionicPlatform.ready(function(){
    if(window.StatusBar){
    StatusBar.styleDefault();
    }
    });
    })

  我们继续,定义一个$scope来携带ToDo list的条目,ToDoListCtrl中声明一个新的$scope变量,如下: 

    .controller('ToDoListCtrl',function($scope){
    $scope.toDoListItems =[{
    task:'Scuba Diving',
    status:'not done'
    },{
    task:'Climb Everest',
    status:'not done'
    }]
    });

  把controllers.js添加到index.html中:

    
    
    {{item.task}}
    
    

  在上面的代码中,我们添加了bar-positive来给应用加颜色。你可以有同样添加很多不同的header。这里有详细的文档: here

我们现在需要在index.html中添加一个button来触发事件:

    
    
    

现在确认一下,在上面的操作中,我们在modal中添加了一个header,一个input box和一个button。

我们同样有需要一个回退的Button在header中,它用来触发 closeModal() 功能。

现在我们开始绑定 ionic modal 到我们的 controller中,我们通过如下的方法把 $ionicModal 注入到controller中:

    angular.module('starter.controllers',[])
    .controller('ToDoListCtrl',function($scope, $ionicModal){
    // array list which will contain the items added
    $scope.toDoListItems =[];
    //init the modal
    $ionicModal.fromTemplateUrl('modal.html',{
    scope: $scope,
    animation:'slide-in-up'
    }).then(function(modal){
    $scope.modal = modal;
    });
    // function to open the modal
    $scope.openModal =function(){
    $scope.modal.show();
    };
    // function to close the modal
    $scope.closeModal =function(){
    $scope.modal.hide();
    };
    //Cleanup the modal when we're done with it!
    $scope.$on('$destroy',function(){
    $scope.modal.remove();
    });
    //function to add items to the existing list
    $scope.AddItem=function(data){
    $scope.toDoListItems.push({
    task: data.newItem,
    status:'not done'
    });
    data.newItem ='';
    $scope.closeModal();
    };
    });

 我们在上面的代码中使用了 .fromTemlateUrl 来加载html的内容,然后在初始化的时候通过两个选项定义了$scope和animation的类型。 

当然我们也定义了打开、关闭moda和添加条目到数组的方法。

运行

好了,万事俱备,虚拟机走起,看起来还不错吧。

总结

在这篇文章中,我们了解了使用Ionic的一个大概流程。你可以在这里看到详细的代码。如果想深入学习,还是应该多了解一下 AngularJS

 参考:大家有兴趣的话,可以阅读这套AngularJS的基础开发教程:AngularJS开发框架实用编程入门之一

来源:http://www.gbtags.com/gb/share/4236.htm

Linux:从把三千行代码重构成15行代码谈起

如果你认为这是一个标题党,那么我真诚的恳请你耐心的把文章的第一部分读完,然后再下结论。如果你认为能够戳中您的G点,那么请随手点个赞。

Linux:从把三千行代码重构成15行代码谈起
Linux:从把三千行代码重构成15行代码谈起

把三千行代码重构为15行

那年我刚毕业,进了现在这个公司。公司是搞数据中心环境监控的,里面充斥着嵌入式、精密空调、总线、RFID的概念,我一个都不懂。还好,公司之前用Delphi写的老客户端因为太慢,然后就搞了个Webform的替代,恰好我对Asp.Net还算了解,我对业务的不了解并不妨碍我称成为这个公司的一个程序员。小公司也有小公司的好,人少,进去很快负责代码开发。我当然也就搞这个数据中心智能管理系统啦。

这个系统非常的庞大,尤其牛逼的是支持客户端组态,然后动态生成网页,数据还能通过Socket实时监控(那时我还真就不懂网络编程)。这个对于当时的我来说,真真是高、大、上呐!!当时跟着了解整个系统大半个月才算能够调试,写一些简单的页面。

在维护系统的过程中,时不时要扩展一些功能,也就接触了下面这个类:

Linux:从把三千行代码重构成15行代码谈起
Linux:从把三千行代码重构成15行代码谈起

看到没有,就是当年最最流行的三层架构的产物,对于刚出茅庐的毛头小子来说,这是多么专业的文件头注释,还有反射也就算了,这构造函数还能静态的,还能私有的?那时刚接触这么高大上的代码的我,瞬间给跪了!

但是,类写多了,我就感觉越来越别扭,就是下面这段代码:

Linux:从把三千行代码重构成15行代码谈起
Linux:从把三千行代码重构成15行代码谈起

每增加一个表,除了要改接口、要改DAL、要改BLL之外,还得在这个工厂类添加一个方法,真真是累到手抽筋,即使有当时公司了的G工给我推荐的神器——动软代码生成器,这粘贴复制的几遍,也是让我感觉到异常繁琐,有时候打键盘稍微累了点,还把复制出来代码改错了,你妹的,难道这就是程序员该干的事情,不,绝对不是!我想起了一句至理名言:当你觉得代码重复出现在程序中的时候,就应该重构了。是的,在这句话的指导下,我开始了折腾,决定挑战这个高大上的代码,事实证明,思想的力量是无穷的。

那么,怎么修改呢,仔细观察之后,发现其中className的生成跟返回的类型非常类似,只是一个是类名,一个是字符串,这两者之间应该能够关联起来。于是google了一下(当时GFW还没猖獗起来哈),隐隐约约就找到了“反射”这两个字,深入了解之后,确定可以完成。

接下来,就是返回的类型了,返回的类型并不固定,但是似乎很有规律……这个似乎好像在哪里见过,对了,模板,C++课程上有讲过的,于是再次google,了解到了C#中使用了泛型代替了C++中的模板。在学习完泛型和反射之后,并参考了网上的一些文章,我捣鼓出了下面的代码:

Linux:从把三千行代码重构成15行代码谈起
Linux:从把三千行代码重构成15行代码谈起

没错,就是它了,三层架构年代最流行的工厂类……

看着原来滚十几屏幕的代码,变成了十多行的代码,真是爽到了骨子里去了,太干净了!唯一让我担忧的是,我进公司的时候,帮忙整理公司申请软件著作权都是需要代码量的,根据代码多少行来评估软件的大小,万一老板知道了我非但没有帮公司增加代码量,还减少了,会不会立即把我开掉?我没敢给我们老板展示我优秀的成果,所幸,这段代码非但没有出过任何问题,还避免了以前同事老是在新增一个类之后,把代码复制过来,但是没有正确修改的问题,大大提高了效率。虽然,我没敢大事宣布我的劳动成果,但是这次成功的修改,则彻底让我走上了代码重构的不归路。

看到这里,大家应该知道这个案例是否真实的了吧。我相信,从08年开始的码农们,看到这种类似的代码绝对不比我少。那么,我想告诉你们的是什么呢?

  • 要在编程过程中多思考
  • 编程的思想很重要,请多看点经典的书
  • 从小处着眼,慢慢重构,尤其在应对一个大型的系统
  • 当重复出现的时候,你应该考虑重构了
  • 粘贴复制的代码越少,你的系统越稳定

少用代码生成器

我们来分析一下,为什么我之前的前辈会写出上面的代码。我归结起来有以下几点:

  • 因为使用了动软代码生成器,生成代码方便,就没多想了。
  • 三层架构的概念倒是了解了,但是没有去深入思考就拿来应用
  • 遇到重复的代码,没有重构的概念,这是思想的问题——思想比你的能力重要

至今为止,还是很多人使用代码生成器,那么我们应该怎么对待这个问题呢。我认为,代码生成器确实可以减少你不少工作,但是少用,那些重复性的工作,除了部分确实是没有办法的,其他大部分都是可以通过框架解决的,举例来说,像三层架构,真正需要用到代码生成器的,也就是Model类而已,其他的完全可以在框架中完成。因此你要竭尽全力的思考怎么在框架中来减少你的重复性工作,而不是依赖于代码生成器

另外,如果你还是在用相关的代码生成工具,请重新定义“动软代码生成器”的代码模板,自己写一个模板;或者使用CodeSmith来完全制定自己的代码生成,因为动软给的代码模板真心乱,比如下面这段代码:

for (int n = 0; n < rowsCount; n++)
{
	model = new DBAccess.Model.eventweek();
	if(dt.Rows[n]["GroupNo"].ToString()!="")
	{
		model.GroupNo=int.Parse(dt.Rows[n]["GroupNo"].ToString());
	}
	if(dt.Rows[n]["Week0"].ToString()!="")
	{
		model.Week0=int.Parse(dt.Rows[n]["Week0"].ToString());
	}
	if(dt.Rows[n]["Week1"].ToString()!="")
	{
		model.Week1=int.Parse(dt.Rows[n]["Week1"].ToString());
	}
}

首先,你就不能用 var row=dt.Rows[n] 替代吗?其次,直接用int.Parse效率多低?再次,dt.Rows[n]["Week0"]为NULL怎么办?

减少造轮子

我们再来看看其他的一些代码:

public List GetDevices(string dev){
	List devs=new List();
	int start=0;
	for(int i=0;i

有没有很眼熟,没错,这就是对String.Split()函数的简单实现。我的前辈应该是从c++程序员转过来的,习惯了各种功能自己实现一遍,但是他忽略了C#的很多东西。我们不去评判这段代码的优劣,而实际上他在很长一段时间都运行得很好。我们来看看使用这一段代码有什么不好的地方:

  • 重复制造了轮子。花费了额外的时间,函数的健壮性和很差
  • 可读性差。其实是一个很简单的功能,但是用上了这么一段函数,起初我还以为有什么特别的功能。

那么,我们应该怎样去避免制造轮子呢?我从个人的经历来提出以下几点,希望能够对各位有所帮助:

  • 了解你所学的编程语言的特性。你可以看一本基础的入门书籍,把所有的特性浏览一遍,或者上MSDN,把相关的内容过一遍。
  • 在你决定动手造轮子之前,先搜索一下现成的解决方案。你还可以到CodeProject、GitHub之类的网站搜索一下。在知乎上有很多大牛其实都在批评,为什么你提问之前,不能首先去搜一下是否有现成的答案,反而指责没有回答他的问题。
  • 你有一定的基础之后,还应该去读一下相关的经典书籍,深入了解其中的原理。比如,你觉得你有一定的基础了,我建议你去吧《CLR Via C#》多读几遍,你了解原理越多,你越是能够利用这编程语言的特性,从而来实现原本那些你认为要靠自己写代码的功能。

这里我再举一个我自己的例子。在我现有的程序中,我发现我需要越来越多的线程来执行一些简单的任务,比如在每天检测一下硬盘是否达到90%了,每天9点要控制一下空调的开启而在网上6点的时候把空调关掉。线程使用越来越多,我越是觉得浪费,因为这些现场仅仅只需完成一次或者有限的几次,大部分时间都是没有意义的,那么怎么办呢?我决定自己写一个任务类,来完成相关的事情。说干就干,我很快把这个类写出来了。

public abstract class MissionBase : IMission
{
    private DateTime _nextExecuteTime;
    protected virtual DateTime[] ExecuteTimePoints { get; private set; }
    protected virtual int IntervalSeconds { get; private set; }
    protected IEngine Engine { get; private set; }
    public bool IsCanceled{get{……}}
    public bool IsExecuting{get{……}}
    public bool IsTimeToExecute{get{……}}
    public abstract bool Enable { get; }
    public abstract string Name { get; }
    protected MissionBase(IEngine engine)
    {
        ExecuteTimePoints = null;//默认采用间隔的方式
        IntervalSeconds = 60 * 60;//默认的间隔为1个小时
        Engine = engine;
    }
    /// 任务的执行方法
    public void Done()
    {
        if (Interlocked.CompareExchange(ref _isExecuting, 1, 0) == 1) return;
        try
        {
			……
        }
        finally
        {
            Interlocked.CompareExchange(ref _isExecuting, 0, 1);
        }
    }
	///实际方法的执行
    protected abstract void DoneReal();
}

但是,实际上这个任务方法,并不好用,要写的代码不少,而且可靠性还没有保障。当然,我可以继续完善这个类,但是我决定搜索一下是否还有其他的方法。直到有一天,我再次阅读《CLR Via C#》,看到线程这一章,讲到了System.Threading.Timmer以及ThreadPoole类时,我就知道了,使用Timer类完全可以解决我的这个用尽量少的线程完成定时任务的问题

因为从原理上来说,Timer类无论你声明了多少个,其实就只有一个线程在执行。当你到了执行时间时,这个管理线程会用ThreadPool来执行Timer中的函数,因为使用的ThreadPool,执行完成之后,线程就马上回收了,这个其实就完全实现了我所需要的功能。

来源:http://www.cnblogs.com/marvin/p/TalkFromReflactingCode3000To15.html

Linux:如何在 Linux 中使用屏幕键盘

屏幕键盘可以作为实体键盘输入的替代方案。在某些时候,屏幕键盘显得非常需要。 比如, 你的键盘刚好坏了;你的机器太多,没有足够的键盘;你的机器没有多余的接口来连接键盘;你是个残疾人,打字有困难;或者你正在组建基于触摸屏的信息服务站。

屏幕键盘也可以作为一种防范实体键盘记录器的保护手段,键盘记录器会悄悄记录按键来获取密码等敏感信息。一些网上银行页面实际上会强制你使用屏幕键盘来增强交易的安全性。

在 linux 中有几个可用的开源键盘软件, 比如 GOK (Gnome 的屏幕键盘)kvkbdonboardFlorence

我会在这个教程中集中讲解 Florence, 告诉你如何用 Florence 设置一个屏幕键盘。 Florence 有着布局方案灵活、输入法多样、自动隐藏等特性。作为教程的一部分,我也将会示范如何只使用鼠标来操作 Ubuntu 桌面

在 Linux 中安装 Florence 屏幕键盘

幸运的是,Florence 存在于大多数 Linux 发行版的基础仓库中。

在 Debian,Ubuntu 或者 Linux Mint 中:

$ sudo apt-get install florence

在 Fedora,CentOS 或者 RHEL (CentOS/RHEL 需要EPEL 仓库) 中:

$ sudo yum install florence

在 Mandriva 或者 Mageia 中:

$ sudo urpmi florence

对于 Archlinux 用户,Florence 存在于 AUR 中。

配置和加载屏幕键盘

当你安装好 Florence 之后,你只需要简单的输入以下命令就能加载屏幕键盘:

$ florence

默认情况下,屏幕键盘总是在其他窗口的顶部,让你能够在任意活动的窗口上进行输入。

在键盘的左侧点击工具按键来改变 Florence 的默认配置。

Linux:如何在 Linux 中使用屏幕键盘
Linux:如何在 Linux 中使用屏幕键盘

在 Florence 的 “样式 (style)” 菜单中,你能够自定义键盘样式,启用/取消声音效果。

Linux:如何在 Linux 中使用屏幕键盘
Linux:如何在 Linux 中使用屏幕键盘

在“窗口 (window)”菜单中,你能够调整键盘背景透明度、按键不透明度,以及控制键盘比例、工具栏、尺寸和总是置顶等特性。如果你的桌面分辨率不是非常高,透明度调整就显得非常有用,因为屏幕键盘会挡住其他窗口。在这个例子中,我切换到透明键盘,并且设置不透明度为 50%。

Linux:如何在 Linux 中使用屏幕键盘
Linux:如何在 Linux 中使用屏幕键盘

在“行为 (behaviour)”菜单中,你能够改变输入方法。Florence 支持几种不同的输入法: 鼠标 (mouse)、触摸屏 (touch screen)、计时器 (timer) 和漫步 (ramble)。鼠标输入是默认输入法。最后的两种输入法不需要按鼠标键。 计时器输入通过将指针滞留在按键上一定时间来触发按键。漫步输入的原理跟计时器输入差不多,但是经过训练和灵巧使用,能够比计时器输入更加迅速。

Linux:如何在 Linux 中使用屏幕键盘
Linux:如何在 Linux 中使用屏幕键盘

在“布局 (layout)”菜单中,你能够改变键盘布局。比如,你能够扩展键盘布局来增加导航键,数字键和功能键。

Linux:如何在 Linux 中使用屏幕键盘
Linux:如何在 Linux 中使用屏幕键盘

来源:https://linux.cn/article-4399-1.html

Linux:Linux有问必答:如何修复“ImportError: No module named scapy.all”

问题:当我运行一个Python应用程序时,出现了这个提示消息“ImportError: No module named scapy.all”。我怎样才能修复这个导入错误呢?

Scapy是一个用Python写的灵活的数据包生成及嗅探程序。使用Scapy,你可以完成创建任意数据包并发送到网络上、从网络上或转储文件中读取数据包、转换数据包等工作。使用Scapy的通用包处理能力,你可以很容易地完成像SYN扫描、TCP路由跟踪以及OS指纹检测之类的工作。你也可以通过Import,将Scapy整合到其它工具中。

该导入错误表明:你还没有在你的Linux系统上安装Scapy。下面介绍安装方法。

Linux:Linux有问必答:如何修复“ImportError: No module named scapy.all”
Linux:Linux有问必答:如何修复“ImportError: No module named scapy.all”

安装Scapy到Debian, Ubuntu或Linux Mint

 $ sudo apt-get install python-scapy

安装Scapy到Fedora或CentOS/RHEL

在CentOS/RHEL上,你首先需要启用EPEL仓库

 $ sudo yum install scapy

源码安装Scapy

如果你的Linux版本没有提供Scapy包,或者你想要试试最新的Scapy,你可以手工使用源码包安装。

下载最新版的Scapy,然后按照以下步骤安装。

$ unzip scapy-latest.zip
$ cd scapy-2.*
$ sudo python setup.py install

via: http://ask.xmodulo.com/importerror-no-module-named-scapy-all.html

译者:GOLinux 校对:wxy

本文由 LCTT 原创翻译,Linux中国 荣誉推出

来源:https://linux.cn/article-4400-1.html

Linux:如何在 Linux 上用 SQL 语句来查询 Apache 日志

Linux 有一个显著的特点,在正常情况下,你可以通过日志分析系统日志来了解你的系统中发生了什么,或正在发生什么。的确,系统日志是系统管理员在解决系统和应用问题时最需要的第一手资源。我们将在这篇文章中着重讲解 Apache HTTP web server 生成的 Apache access 日志。

这次,我们会通过另类的途径来分析 Apache access 日志,我们使用的工具是 asql。asql 是一个开源的工具,它能够允许使用者使用 SQL 语句来查询日志,从而通过更加友好的格式展现相同的信息。

Linux:如何在 Linux 上用 SQL 语句来查询 Apache 日志
Linux:如何在 Linux 上用 SQL 语句来查询 Apache 日志

Apache 日志背景知识

Apache 有两种日志:

  • Access log:存放在路径 /var/log/apache2/access.log (Debian) 或者 /var/log/httpd/access_log (Red Hat)。Access Log 记录所有 Apache web server 执行的请求。
  • Error log:存放在路径 /var/log/apache2/error.log (Debian) 或者 /var/log/httpd/error_log (Red Hat)。Error log 记录所有 Apache web server 报告的错误以及错误的情况。Error 情况包括(不限于)403(Forbidden,通常在请求被拒绝访问时被报告),404(Not found,在请求资源不存在时被报告)。

虽然管理员可以通过配置 Apache 的配置文件来自定义 Apache access log 的详细程度,不过在这篇文章中,我们会使用默认的配置,如下:

远程 IP - 请求时间 - 请求类型 - 响应代码 - 请求的 URL - 远程的浏览器信息 (也许包含操作系统信息)

因此一个典型的 Apache 日志条目就是下面这个样子:

192.168.0.101 - - [22/Aug/2014:12:03:36 -0300] "GET /icons/unknown.gif HTTP/1.1" 200 519 "http://192.168.0.10/test/projects/read_json/" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:30.0) Gecko/20100101 Firefox/30.0"

但是 Apache error log 又是怎么样的呢?因为 error log 条目主要记录 access log 中特殊的请求(你可以自定义),所以你可以通过 access log 来获得关于错误情况的更多信息(example 5 有更多细节)。

此外要提前说明的, access log 是系统级别的日志文件。要分析虚拟主机的日志文件,你需要检查它们相应的配置文件(例如: 在 /etc/apache2/sites-available/[virtual host name] 里(Debian))。

在 Linux 上安装 asql

asql 由 Perl 编写,而且需求以下两个 Perl 模块:SQLite 的 DBI 驱动以及 GNU readline。

在 Debian, Ubuntu 以及其衍生发行版上安装 asql

使用基于 Debian 发行版上的 aptitude,asql 以及其依赖会被自动安装。

# aptitude install asql

在 Fedora,CentOS,RHEL 上安装 asql

在 CentOS 或 RHEL 上,你需要启用 EPEL repository,然后运行以下代码。在 Fedora 中,直接运行以下代码:

# sudo yum install perl-DBD-SQLite perl-Term-Readline-Gnu
# wget http://www.steve.org.uk/Software/asql/asql-1.7.tar.gz
# tar xvfvz asql-1.7.tar.gz
# cd asql
# make install

asql 是如何工作的?

从上面代码中的依赖中你就可以看出来,asql 转换未结构化的明文 Apache 日志为结构化的 SQLite 数据库信息。生成的 SQLite 数据库可以接受正常的 SQL 查询语句。数据库可以通过当前以及之前的日志文件生成,其中也包括压缩转换过的日志文件,类似 access.log.X.gz 或者 access_log.old。

首先,从命令行启动 asql:

# asql

你会进入 asql 内置的 shell 交互界面。

输入 help 列表可执行的命令:

Linux:如何在 Linux 上用 SQL 语句来查询 Apache 日志
Linux:如何在 Linux 上用 SQL 语句来查询 Apache 日志

首先在 asql 中加载所有的 access 日志:

asql > load 

比如在 Debian 下:

asql > load /var/log/apache2/access.*

在 CentOS/RHEL 下:

asql > load /var/log/httpd/access_log*

当 asql 完成对 access 日志的加载后,我们就可以开始数据库查询了。注意一下,加载后生成的数据库是 “temporary” (临时)的,意思就是数据库会在你退出 asql 的时候被清除。如果你想要保留数据库,你必须先将其保存为一个文件。我们会在后面介绍如何这么做(参考 example 3 和 4)。

生成的数据库有一个名为 logs 的表。输入下面的命令列出 logs 表中提供的域:

Linux:如何在 Linux 上用 SQL 语句来查询 Apache 日志
Linux:如何在 Linux 上用 SQL 语句来查询 Apache 日志

一个名为 .asql 的隐藏文件,保存于用户的 home 目录下,记录用户在 asql shell 中输入的命令历史。因此你可以使用方向键浏览命令历史,按下 ENTER 来重复执行之前的命令。

asql 上的示例 SQL 查询

下面是几个使用 asql 针对 Apache 日志文件运行 SQL 查询的示例:

Example 1:列出在 2014 年 10 月中请求的来源 / 时间以及 HTTP 状态码。

SELECT source, date, status FROM logs WHERE date >= '2014-10-01T00:00:00' ORDER BY source;

Example 2:从小到大显示单个客户端处理的请求大小(bytes)。

SELECT source, SUM(size), AS NUMBER FROM logs GROUP BY source ORDER BY Number DESC;

Example 3:在当前目录中保存数据库为 [filename]。

save [filename]

这样做可以避免使用 load 命令对日志的语法分析所占用的处理时间。

Example 4:在重新进入 asql 后载入数据库。

restore [filename]
Linux:如何在 Linux 上用 SQL 语句来查询 Apache 日志
Linux:如何在 Linux 上用 SQL 语句来查询 Apache 日志

Example 5:返回 access 日志中记录的 error 情况。在这个例子中,我们将显示所有返回 HTTP 状态码为 403(access forbidden)的请求。

SELECT source, date, status, request FROM logs WHERE status='403' ORDER BY date

这个例子想要表现的是:虽然 asql 只分析 access 日志,我们还是可以通过使用请求的状态域来显示有 error 情况的请求。

小结:

我们体验了 asql 如何帮助我们分析 Apache 日志文件,并将结果通过友好的格式输出。虽然你也可以通过使用命令行的工具(例如 cat 与 grep,uniq,sort,wc 等等之间的管道)来实现类似功能,与此比较起来 asql 展示了它如同瑞士军刀一般的强大功能,使我们在自己的需求下能够通过标准 SQL 查询语句来过滤日志。

希望这篇教程能帮助到你们。

请不要拘束地将评论文章,分享文章,提出疑问。


via: http://xmodulo.com/sql-queries-apache-log-files-linux.html

作者:Gabriel Cánepa 译者:ThomazL 校对:wxy

本文由 LCTT 原创翻译,Linux中国 荣誉推出

来源:https://linux.cn/article-4405-1.html

Linux:同行代码审查(Peer Code Review)实战经验

我有时候会听到我们的团队成员这样议论:

“项目的Code review 只是浪费时间。”

“我没有时间做Code review。”

“我的发布时间延迟了,因为我的同事还没有完成我代码的Code review。”

“你相信我的同事居然要求我对我的代码做修改吗?请跟他们说代码中的一些联系会被打断——如果在我原来代码的基础之上做修改的话。”

(LCTT 译注:Code Review中文可以翻译成代码审查,一般由开发待review的代码的成员以外的团队成员来进行这样的工作。由于是专业术语,没有将Code review用中文代替。)

Linux:同行代码审查(Peer Code Review)实战经验
Linux:同行代码审查(Peer Code Review)实战经验

为什么要做Code review?

每个专业软件开发者都有一个重要的目标:持续的提高他们的工作质量。即使你团队中都是一些优秀的程序员,但是你依然不能将你自己与一个有能力的自由职业者区分开来,除非你从团队的角度来工作。Code review是团队工作的一个重要的方面。尤其是:

代码复查者(reviewer)能从他们的角度来发现问题并且提出更好的解决方案。

确保至少你团队的另一个其他成员熟悉你的代码,通过给新员工看有经验的开发者的代码能够某种程度上提高他们的水平。

公开reviewer和被复查者的想法和经验能够促进团队间的知识的分享。

能够鼓励开发者将他们的工作进行的更彻底,因为他们知道他们的代码将被其他的人阅读。

在review的过程中的注意点

但是,由于Code review的时间有限,上面所说的目标未必能全部达到。就算只是想要打一个补丁,都要确保意图是正确的。如果只是将变量名改成骆驼拼写法(camelCase),那不算是code review。在开发过程中进行结对编程是有益处的,它能够使两个人得到公平的锻炼。你能够在code review上花许多时间,并且仍然能够比在结对编程中使用更少的时间。

我的感受是,在项目开发的过程中,25%的时间应该花费在code review上。也就是说,如果开发者用两天的时间来开发一个东西,那么复查者应该使用至少四个小时来审查。

当然,只要你的review结果准确的话,具体花了多少时间就显得不是那么的重要。重要的是,你能够理解你看的那些代码。这里的理解并不是指你看懂了这些代码书写的语法,而是你要知道这段代码在整个庞大的应用程序、组件或者库中起着什么样的作用。如果你不理解每一行代码的作用,那么换句话说,你的code review就是没有价值的。这就是为什么好的code review不能很快完成的原因。需要时间来探讨各种各样的代码路径,让它们触发一个特定的函数,来确保第三方的API得到了正确的使用(包括一些边缘测试)。

为了查阅你所审查的代码的缺陷或者是其他问题,你应该确保:

  • 所有必要的测试都已经被包含进去。

  • 合理的设计文档已经被编写。

再熟练的开发者也不是每次都会记得在他们对代码改动的时候把测试程序和文档更新上去。来自reviewer的一个提醒能够使得测试用例和开发文档不会一直忘了更新。

避免code review负担太大

如果你的团队没有强制性的code review,当你的code review记录停留在无法管理的节点上时会很危险。如果你已经两周没有进行code review了,你可以花几天的时间来跟上项目的进度。这意味着你自己的开发工作会被阻断,当你想要处理之前遗留下来的code review的时候。这也会使得你很难再确保code review的质量,因为合理的code review需要长期认真的努力,最终会很难持续几天都保持这样的状态。

由于这个原因,开发者应当每天都完成他们的review任务。一种好办法就是将code review作为你每天的第一件事。在你开始自己的开发工作之前完成所有的code review工作,能够使你从头到尾都集中注意力。有些人可能更喜欢在午休前或午休后或者在傍晚下班前做review。无论你在哪个时间做,都要将code review看作你的工作之一并且不能分心,你要避免:

  • 没有足够的时间来处理你的review任务。

  • 由于你的code review工作没有做完导致版本的推迟发布。

  • 提交不再相关的review,由于代码在你review期间已经改动太大。

  • 因为你要在最后一分钟完成他们,以至于review质量太差。

书写易于review的代码

有时候review没有按时完成并不都是因为reviewer。如果我的同事使用一周时间在一个大工程中添加了一些乱七八糟的代码,且他们提交的补丁实在是太难以阅读。在一段代码中有太多的东西要浏览。这样会让人难以理解它的作用,自然会拖慢review的进度。

为什么将你的工作划分成一些易于管理的片段很重要有很多原因。我们使用scrum方法论(一种软件开发过程方法),因此对我们来说一个合理的单元就是一个story。通过努力将我们的工作使用story组织起来,并且只是将review提交到我们正在工作的story上,这样,我们写的代码就会更加易于review。你们也可以使用其他的软件开发方法,但是目的是一样的。

书写易于review的代码还有其他先决条件。如果要做一些复杂的架构决策,应该让reviewer事先知道并参与讨论。这会让他们之后review你们的代码更加容易,因为他们知道你们正在试图实现什么功能并且知道你们打算如何来实现。这也避免了开发者需要在reviewer提了一个不同的或者更好的解决方案后大片的重写代码。

项目需要应当在设计文档中详细的描述。这对于一个项目新成员想要快速上手并且理解现有的代码来说非常重要。这从长远角度对于一个reviewer来说也非常有好处。单元测试也有助于reviewer知道一些组件是怎么使用的。

如果你在你的补丁中包含的第三方的代码,记得单独的提交它。当jQuery的9000行代码被插入到了项目代码的中间,毫无疑问会造成难以阅读。

创建易读的review代码的另一个非常重要的措施是添加相应的注释代码。这就要求你事先自己做一下review并且在一些你认为会帮助reviewer进行review的地方加上相应的注释。我发现加上注释相对于你来说往往只需要很短的时间(通常是几分钟),但是对于review来说会节约很多的时间。当然,代码注释还有其他相似的好处,应该在合理的地方使用,但往往对code review来说更重要。事实上,有研究表明,开发者在重读并注释他们代码的过程中,通常会发现很多问题。

代码大范围重构的情况

有时候,有必要重构一段代码使其能够作用于多个其他组件。若是一个大型的应用要这样做,会花费几天甚至是更多的时间,结果是生成一个诺大的补丁包。在这种情况下,进行一个标准的code review可能是不切实际的。

最好的方法是增量重构你的代码。找出合理范围内的一部分改变,以此为基础来重构。一旦修改和review完成,进入第二个增量。以此类推,直到整个重构完成。这种方法可能不是在所有的情况下都可行,但是尽管如此,也能避免在重构时出现大量的单片补丁。开发者使用这种方式重构可能会花去更多的时间,但这也使得代码质量更高并且之后的review会更简单。

如果实在是没有条件去通过增量方式重构代码(有人可能会说之前的代码书写并组织的是多么的好),一种解决方案是在重构时进行结对编程来代替code review。

解决团队成员之间的纠纷

你的团队中都是一些有能力的专家,在一些案例中,完全有可能因为对一个具体编码问题的意见的不同而产生争论。作为一个开发者,应该保持一个开发的头脑并且时刻准备着妥协,当你的reviewer更想要另一种解决方法时。不要对你的代码持有专有的态度,也不要自己持有审查的意见。因为有人会觉得你应该将一些重复的代码写入一个能够复用的函数中去,这并不意味着这是你的问题。

作为一个reviewer,要灵活。在提出修改建议之前,考虑你的建议是否真的更好或者只是无关紧要。如果你把力气和注意力花在那些原来的代码会明确需要改进的地方会更加成功。你应该说”它或许值得考虑…”或者”一些人建议…”而不是”我的宠物都能写一个比这个更加有效的排序方法”。

如果你真的决定不了,那就询问另一个你及你所审查的人都尊敬的开发者来听一下你意见并给出建议。


via: http://blog.salsitasoft.com/practical-lessons-in-peer-code-review/

作者:Matt 译者:john 校对:wxy

本文由 LCTT 原创翻译,Linux中国 荣誉推出

来源:https://linux.cn/article-4406-1.html

Linux:反射是否真的会让你的程序性能降低?

早两天写了《从把三千行代码重构成15行代码谈起》这篇文章,看到评论中有一些同学的回复还是在质疑反射的性能,好像程序用上了反射,就像开上了拖拉机似的。本来我觉得这个话题没有什么好讨论的了,网上已经有太多太多的文章在说这个问题,有疑问的大可以到网上找相关的文章来查阅。但是,我想起来我刚编程的时候,也是遇到这种困惑到网上一查找,从各种角度阐述的都有,本质基本都说出来了,但是还是有很多人不理解,我这里就从我的角度再说一遍。

Linux:反射是否真的会让你的程序性能降低?
Linux:反射是否真的会让你的程序性能降低?

反射肯定比直接调用慢

这个毋庸置疑了,我这篇文章也不是证明反射有多高效的。

现在的快递哥很火,那我们就举个快递的例子。如果快递员就在你住的小区,那么你报一个地址:xx栋xx号,那么快递员就可以马上知道你在哪里,直接就去到你家门口;但是,如果快递员是第一次来你们这里,他是不是首先得查查百度地图,看看怎么开车过去,然后到了小区是不是得先问问物管xx栋怎么找,然后,有可能转在楼下转了两个圈才到了你的门前。

我们看上面这个场景,如果快递员不熟悉你的小区,是不是会慢点,他的时间主要花费在了查找百度地图,询问物业管理。OK,反射也是一样,因为我事先什么都不知道,所以我得花时间查询一些其他资料,然后我才能找到你。大家有兴趣可以查看反射的实现原理,以及MetaData的相关概念。

反射到底比直接调用慢多少?

好了,我们知道反射肯定慢的,那么是不是反射就不能用了呢?有些人一听到慢,就非常着急的下结论,反射怎样怎样不行,怎样怎样不能用。但是,同学,反射到底比直接调用慢多少,你造吗,能给我个实际的数据吗?很多人其实对性能只有个模糊的概念,而没有数值支撑。之前我给同事找了一个动态解析表达式的类库,他觉得不太好用,他很聪明,很快的找到了用DataTale.Compute可以实现公式的动态解析。我问他,这个方法和我给的类库性能上有什么区别?他跟我说,这个已经很快了,执行1秒都不到。我一听,就觉得不对劲,你的思想还停留在秒级,跟我谈什么性能?

怎么去判断一个函数的性能?因为函数的执行太快太快了,你需要一个放慢镜,这样才能捕捉到他的速度。怎么做?把一个函数执行一百万遍或者一千万遍,你才能真正了解一个函数的性能。也就是,你如果想判断性能,你就不能还停留在秒级,毫秒级的概念,你必须用另外一个概念替代,才能知道真正的性能。结果我同事把这两种方法执行了100w遍,确实,我提供的类库比他的快了8秒。

好了,现在拿我早两天提供的工厂方法来做测试,其中CodeTimer的实现参考赵大神的文章《一个简单的性能计数器:CodeTimer》:

测试方法如下:

[Test]
    public void TestReflector()
    {
        CodeTimer.Time("Direct", 100 * 10000,
            () =>
            {
                var instance = new ConnectionTest();
            });
        CodeTimer.Time("Reflect", 100 * 10000,
            () =>
            {
                this.GetType().Assembly.CreateInstance("TestPropertyGrid.ConnectionTest");
            });
    }

测试结果如下:

Direct
	Time Elapsed:	25ms
	CPU Cycles:	57,582,163
	Gen 0: 		14
	Gen 1: 		0
Reflect
	Time Elapsed:	3,231ms
	CPU Cycles:	8,001,720,795
	Gen 0: 		269
	Gen 1: 		1

看到没,我们的放大镜起作用了,现在我们大概可以下这么一个结论:在执行100万遍的时候,反射大概把直接调用慢50~100倍。100倍,咋一看,是相差很大的,但是,我前文说了,别着急下结论,你要看看前提条件。自古我们就喜欢断章取义,比如“以德报怨”这个成语,好像古人说让我们遇到不好的,你不能怨恨,要更好的对待他人,别人打你左脸一巴掌,你应该把右脸伸过去让他再打一下。但实际这个成语是怎样的呢?

或曰:“以德报怨,何如?”子曰:“何以报德?以直报怨,以德报德”

老孔的意思其实是如果别人对你好,那么你就对他好,要是他招你惹你了,你就干他娘的!你看,傻眼了吧?

有多少情况下需要考虑反射带来的影响?

我认为这个情况是非常非常少的,绝大多数的我们根本就无需考虑这个。就上我上一篇文章提到的工厂,你程序有多少个实体,有100万个吗?如果你只是在弹出窗口的时候new一下,这个百万分之十秒的影响对你很重要吗?

另外,有些人讲,我要是真有这种需求,要把一个对象new一百万遍,那不还是慢吗?这种情况有没有,有!比如我有100w条记录,需要取出来,然后通过反射赋值到一个Model类中。

但是对于这种情况,如果你真是这么想的话,我只能说,你坐办公室坐久了,脑袋生锈了,该去爬爬山,泡泡妞了。如果你需要对一个对象反射一百万遍,那么你就应该缓存这个对象了。拿我们上面那个例子来说,如果这个快递员给小区的人送一百万遍的快递还认不得路,每次都还得百度地图,然后问物业管理,你丫的你还没把他开掉了,那你脑袋不是秀逗了,要不就是任性的有钱人。

上面代码如果缓存之后执行一百万遍,跟直接调用有多大的区别?我这里就不贴代码了,免得你们直接看结果没有意思,自己把代码敲一遍,印象更深刻。

那么,还有没有更快的办法,有。比如你的快递员开始用的是IPHONE4,现在可以考虑给他买个6+。在.net中,提供了Emit的相关方法来让你更快的反射。这里送你一个通过反射快速给Model赋值的轮子“Dapper”,自己回家造去。

编程中是否应该使用反射?

其实看完上面的文字,我相信你们都有了一个初步的判断,而我的看法是:绝大多数的情况下你都可以用反射

如果你觉得是因为反射导致你程序慢的话,那么,请先用放慢镜好好观察一下,到底是不是反射的问题。如果你确定是反射的问题,那么你再好好的考虑下是不是你没有用对反射,是不是像上面那个走了一百万遍都不认识路的快递员一样。最后,如果你觉得性能上还是不够,那么我建议你升级下硬件吧,把硬件性能上升个3%总好过你请个牛逼的工程师来帮你做这种极限的优化,有一句话我觉得很对“工程师比服务器要昂贵的多”。如果你还非得跟我较劲,那么,没办法了,你程序对性能的要求已经超出了本文讨论的范畴,如果你真有这种需求了,我觉得你也没有必要看我这篇文章了,因为你已经足够牛逼到对系统语言都有深入了解了。

大多时候,我们会把程序的性能归结于编程语言,或者使用了反射等技术,而甚少去关心自己的代码,这种心态会导致你技术的发展越来越缓慢,因为你已经失去了求知的欲望,以及一颗追求技术进步的心。请你记住,更多的时候,影响我们程序性能的,是你编程的思想,你对待编码的态度!

总结

好吧,说了这么多,估计很多人直接就拖到文章末尾然后因为文章码了这么多字而默默点了个赞,那么,我在最后给大家奉献一下本文的精华:

  1. 反射大概比直接调用慢50~100倍,但是需要你在执行100万遍的时候才会有所感觉
  2. 判断一个函数的性能,你需要把这个函数执行100万遍甚至1000万遍
  3. 如果你只是偶尔调用一下反射,请忘记反射带来的性能影响
  4. 如果你需要大量调用反射,请考虑缓存。
  5. 你的编程的思想才是限制你程序性能的最主要的因素

来源:http://www.cnblogs.com/marvin/p/ShallWeUseReflect.html

Linux:如何在Linux上构建 RAID 10阵列

RAID 10阵列(又名RAID 1+0 或先镜像后分区)通过结合RAID 0 (读写操作在多个磁盘上同时并行执行)和RAID 1(数据被完全相同地写入到两个或更多的磁盘)两者的特点实现高性能和高容错性的磁盘I/O。

这篇文章会指导你如何使用五块相同的8GB磁盘来组成一个软件RAID 10阵列。因为组成一个RAID 10阵列至少需要4块磁盘(比如,两个镜像各有一对分区组合),而且需要添加一块额外的备用磁盘以防某块主要的磁盘出错。本文也会分享一些工具,在稍后用来分析RAID阵列的性能。

注意RAID 10的优缺点和其它分区方法(在不同大小的磁盘和文件系统上)的内容不在本文讨论范围内。

Linux:如何在Linux上构建 RAID 10阵列
Linux:如何在Linux上构建 RAID 10阵列

Raid 10 阵列如何工作?

如果你需要实现一种支持I/O密集操作(比如数据库、电子邮件或web服务器)的存储解决方案,RAID 10就是你需要的。来看看为什么这么说,请看下图。

Linux:如何在Linux上构建 RAID 10阵列
Linux:如何在Linux上构建 RAID 10阵列

上图中的文件由A、B、C、D、E和F六种块组成,每一个RAID 1镜像对(如镜像1和2)在两个磁盘上复制相同的块。在这样的配置下,写操作性能会因为每个块需要写入两次而下降,每个磁盘各一次;而读操作与从单块磁盘中读取相比并未发生改变。不过这种配置的好处是除非一个镜像中有超过一块的磁盘故障,否则都能保持冗余以维持正常的磁盘I/O操作。

RAID 0的分区通过将数据划分到不同的块,然后执行同时将块A写入镜像1、将块B写入镜像2(以此类推)的并行操作以提高整体的读写性能。在另一方面,没有任何一个镜像包含构成主存的数据片的全部信息。这就意味着如果其中一个镜像故障,那么整个RAID 0组件将无法正常工作,数据将遭受不可恢复的损失。

建立RAID 10阵列

有两种建立RAID 10阵列的可行方案:复杂法(一步完成)和嵌套法(先创建两个或更多的RAID 1阵列,然后使用它们组成RAID 0)。本文会讲述复杂法创建RAID 10阵列的过程,因为这种方法能够使用偶数或奇数个磁盘去创建阵列,而且能以单个RAID设备的形式被管理,而嵌套法则恰恰相反(只允许偶数个磁盘,必须以嵌套设备的形式被管理,即分开管理RAID 1和RAID 0)。

假设你的机器已经安装mdadm,并运行着相应的守护进程,细节参见这篇文章。也假设每个磁盘上已经划分出一个主分区sd[bcdef]1 (LCTT 译注:共计五块磁盘,这里是从sdb – sdf)。使用命令:

ls -l /dev | grep sd[bcdef]

查看到的输出应该如下所示:

Linux:如何在Linux上构建 RAID 10阵列
Linux:如何在Linux上构建 RAID 10阵列

然后使用下面的命令创建一个RAID 10阵列(LCTT 译注:使用了四块磁盘 bcde 创建):

 # mdadm --create --verbose /dev/md0 --level=10 --raid-devices=4 /dev/sd[bcde]1 --spare-devices=1 /dev/sdf1

当阵列创建完毕后(最多花费几分钟),执行命令

# mdadm --detail /dev/md0

的输出应如下所示:

Linux:如何在Linux上构建 RAID 10阵列
Linux:如何在Linux上构建 RAID 10阵列

在更进一步之前需要注意以下事项。

  1. Used Dev Space表示阵列所使用的每一块磁盘的容量。

  2. Array Size表示阵列的整体大小。RAID 10阵列的大小通过(N*C)/M计算,其中N是活跃磁盘的数目,C是每个活跃磁盘的容量,M是每一个镜像中磁盘的数目。在本文的情形下,这个值等于(4*8GiB)/2 = 16GiB。

  3. Layout是整个数据布局的详细信息。可能的布局数值如下所示。


  • n(默认选项):代表就近(near)拷贝。一个数据块的多个拷贝在不同磁盘里有相同的偏移量。这种布局提供和RAID 0阵列相似的读写性能。
Linux:如何在Linux上构建 RAID 10阵列
Linux:如何在Linux上构建 RAID 10阵列
  • o代表偏移量(offset)拷贝。块并不是在条带里面复制的,而是整个条带一起复制,但是循环会打乱,所以同一个分区中复制的块会出现在不同的磁盘。因此,一个块的后续拷贝会出现在下一个磁盘中,一个块接着一个块。为了在RAID 10阵列中使用这种布局,在创建阵列的命令中添加–layout=o2选项。
Linux:如何在Linux上构建 RAID 10阵列
Linux:如何在Linux上构建 RAID 10阵列
  • f代表远端(far)拷贝(多个拷贝在不同的磁盘中具有不同的偏移量)。这种布局提供更好的读性能但带来更差的写性能。因此,对于读远远多于写的系统来说是最好的选择。为了在RAID 10阵列中使用这种布局,在创建阵列的命令中添加–layout=f2。
Linux:如何在Linux上构建 RAID 10阵列
Linux:如何在Linux上构建 RAID 10阵列

跟在布局选项nfo后面的数字代表所需的每一个数据块的副本数目。默认值是2,但可以是2到阵列中磁盘数目之间的某个值。提供足够的副本数目可以最小化单个磁盘上的I/O影响。

  1. Chunk Size,参考Linux RAID wiki的说明,是写入磁盘的最小数据单元。最佳的chunk大小取决于I/O操作的速率和相关的文件大小。对于大量的写操作,通过设置相对较大的chunk可以得到更低的开销,但对于主要存储小文件的阵列来说更小的chunk性能更好。为了给RAID 10指定一个chunk大小,在创建阵列的命令中添加–chunk=desiredchunksize

不幸的是,并没有设置一个大小就能适合全局的策略来提高性能,但可以参考下面的一些方案。

  • 文件系统:就整体而言,XFS据说是最好的,当然EXT4也是不错的选择。
  • 最佳布局:远端布局能提高读性能,但会降低写性能。
  • 副本数目:更多的副本能最小化I/O影响,但更多的磁盘需要更大的花费。
  • 硬件:在相同的环境下,SSD比传统(机械旋转)磁盘更能带来出性能提升

来源:https://linux.cn/article-4416-1.html

Linux:2014年12月最棒的 15 个 JavaScript 库

JavaScript提供了丰富的函数库,不管是事件,效果,还是AJAX。如果偶尔出现JavaScript库没法做某样工作,那一定会有一个插件可以做到!

这种库提供的灵活性用起来杠杠的。不过,它们其中的一些有时却受到文件大小的阻碍。还有,尽管我们在函数设计上可以采取一些知名的 JavaScript库,但是如果你首先想到使用一个更加轻量级的,更加专注的库来执行一个具体的任务,或者,这个任务不被很多流行的 JavaScript库所支持,那这会是一个非常明智的决定。 

在这篇文章里,我们将介绍几个 2014 年 12 月最适用的 JavaScript 和 Jquery 库。 它们可能对你当前的任务,或者你正在为你的站点遇到的头疼的问题提供很棒的解决方法。希望你会觉得这个列表很方便,帮你发现一些在你将来的项目中有用的插件。

1. Julius JS

Linux:2014年12月最棒的 15 个 JavaScript 库
Linux:2014年12月最棒的 15 个 JavaScript 库
JuliusJS 是一个Web的语音识别库。 它是Juluis自已用的一个JavaScript端口。它不断地监听用户的正在说的话,然后在callback中解码。所有的识别都在浏览器的一个worker中执行。基于MIT License。 

2. Vue.js

Linux:2014年12月最棒的 15 个 JavaScript 库
Linux:2014年12月最棒的 15 个 JavaScript 库
Vue.js 旨在打造交互式的Web接口。 它简单,灵活的API提供了MVVM数据绑定和组件化系统的好处。在技术上,Vue.js专注于MVVM模式的视图层。通过双向数据绑定来关联视图和模式。真正的Dom操作和输出格式被抽象为Directives和Filters。

3. MetricsGraphics.js

Linux:2014年12月最棒的 15 个 JavaScript 库
Linux:2014年12月最棒的 15 个 JavaScript 库
MetricsGraphics.js 是一个建立在 D3 基础上,为可视化和时间序列化的数据而优化的库。它提供了一种简单的方式,用一致性,持久性,和响应式方式来产生相同类型的图形。这个库现在支持折线图,, 散点图,直方图,还有像地毯图和基本的线性回归图。

4. SVG Morpheus

Linux:2014年12月最棒的 15 个 JavaScript 库
Linux:2014年12月最棒的 15 个 JavaScript 库
SVG Morpheus 是一个使SVG图标改变形状的JavaScript库。 它实现了Material Design’s 的一些非常棒的细节过渡。

5. Contents

Linux:2014年12月最棒的 15 个 JavaScript 库
Linux:2014年12月最棒的 15 个 JavaScript 库
Contents 使自动在document里创建基于表头的表格变得很简单。TOC的结果是生成一个有序的列表,插入到#contents容器。

6. Vivus

Linux:2014年12月最棒的 15 个 JavaScript 库
Linux:2014年12月最棒的 15 个 JavaScript 库
Vivus 是一个轻量级的JavaScript类(完全无依赖) 来允许你创建 SVG 动画,让他们显示被画出来的轨迹。 Vivus提供很多不同种类的动画。 另外还有选项,你可以按你的想法来创建一个定制的脚本来创建你的SVG动画。

7. Particles.js

Linux:2014年12月最棒的 15 个 JavaScript 库
Linux:2014年12月最棒的 15 个 JavaScript 库
Particles.js 一个轻量级的JavaScript库,用来创建颗粒。

##########NextPage[title=]##########

8. Zip.js

Linux:2014年12月最棒的 15 个 JavaScript 库
Linux:2014年12月最棒的 15 个 JavaScript 库
zip.js 是一个开源的JavaScript库,用来压缩和解压文件。zip.js提供了一个底层API来读写大的zip文件。zip.js在Chrome, Firefox, Safari 6 和Internet Explorer 10工作非常正常。

9. Melchior.js

Linux:2014年12月最棒的 15 个 JavaScript 库
Linux:2014年12月最棒的 15 个 JavaScript 库
Melchior.js 是一个模式加载器,提供通过链式模块定义的API,让你可以远离那些长长的AMD定义和重复的模块名。它起步非常容易,定制方便,而且压缩版只有3KB。

10. Lining.js

Linux:2014年12月最棒的 15 个 JavaScript 库
Linux:2014年12月最棒的 15 个 JavaScript 库
Lining.js 一个简单的JavaScript插件,它为基本的Web排版提供了一个完善的DOWN-TO-THE-LINE控制。

11. Screenful.js

Linux:2014年12月最棒的 15 个 JavaScript 库
Linux:2014年12月最棒的 15 个 JavaScript 库
Screenful.js 是一个简单的跨平台的全屏API封装。它使不同浏览器的全屏实现变得很平滑。而且可以让任何元素全屏。

12. Four Shadows

Linux:2014年12月最棒的 15 个 JavaScript 库
Linux:2014年12月最棒的 15 个 JavaScript 库
Four Shadows 使你可以在图标或其他元素加入time-aware阴影。早上9 0’clock绝对不会出现5 o’clock方向的阴影。

13. Binoculars

Linux:2014年12月最棒的 15 个 JavaScript 库
Linux:2014年12月最棒的 15 个 JavaScript 库
Binoculars 是一个开源的数据捕获库。主要目的是为了让所有的数据都能轻松获取。目前,它主要应用在捕获HTML5视频元素的数据。

14. jTinder

Linux:2014年12月最棒的 15 个 JavaScript 库
Linux:2014年12月最棒的 15 个 JavaScript 库
jTinder 是一个使对人物,商品,图片投票变得快速和简单的JavaScript库。它为移动触摸设备进行了优化,不过也有桌面版。

15. Atomus

atomus Atomus 是一个简单的工具库,用来在Node.js环境下测试客户端代码。当使用终端模拟浏览器的时候,Atomus在单元测试和功能性测试中非常有用。这就是Atomus名字的来源。Atomus会和应用程序的单元和谐工作。你只需要包含需要测试框架和模块,然后创建一个实例,然后开始使用DOM和模块的API即可。

来源:http://www.oschina.net/translate/top-15-javascript-libraries-december-2014

Linux:浅析 Linux 初始化 init 系统: sysvinit

近年来,Linux 系统的 init 进程经历了两次重大的演进,传统的 sysvinit 已经淡出历史舞台,新的 init 系统 UpStart 和 systemd 各有特点,而越来越多的 Linux 发行版采纳了 systemd。本系列简要介绍了这三种 init 系统的使用和原理,每个 Linux 系统管理员和系统软件开发者都应该了解它们,以便更好地管理系统和开发应用。本文是系列的第一部分,主要讲述 sysvinit 的特点和使用。

什么是 Init 系统,init 系统的历史和现状

Linux 操作系统的启动首先从 BIOS 开始,接下来进入 boot loader,由 bootloader 载入内核,进行内核初始化。内核初始化的最后一步就是启动 pid 为 1 的 init 进程。这个进程是系统的第一个进程。它负责产生其他所有用户进程。

init 以守护进程方式存在,是所有其他进程的祖先。init 进程非常独特,能够完成其他进程无法完成的任务。

Init 系统能够定义、管理和控制 init 进程的行为。它负责组织和运行许多独立的或相关的始化工作(因此被称为 init 系统),从而让计算机系统进入某种用户预订的运行模式。

仅仅将内核运行起来是毫无实际用途的,必须由 init 系统将系统代入可操作状态。比如启动外壳 shell 后,便有了人机交互,这样就可以让计算机执行一些预订程序完成有实际意义的任务。或者启动 X 图形系统以便提供更佳的人机界面,更加高效的完成任务。这里,字符界面的 shell 或者 X 系统都是一种预设的运行模式。

大多数 Linux 发行版的 init 系统是和 System V 相兼容的,被称为 sysvinit。这是人们最熟悉的 init 系统。一些发行版如 Slackware 采用的是 BSD 风格 Init 系统,这种风格使用较少,本文不再涉及。其他的发行版如 Gentoo 是自己定制的。Ubuntu 和 RHEL 采用 upstart 替代了传统的 sysvinit。而 Fedora 从版本 15 开始使用了一个被称为 systemd 的新 init 系统。

可以看到不同的发行版采用了不同的 init 实现,本系列文章就是打算讲述三个主要的 Init 系统:sysvinit,UpStart 和 systemd。了解它们各自的设计特点,并简要介绍它们的使用。

在 Linux 主要应用于服务器和 PC 机的时代,SysVinit 运行非常良好,概念简单清晰。它主要依赖于 Shell 脚本,这就决定了它的最大弱点:启动太慢。在很少重新启动的 Server 上,这个缺点并不重要。而当 Linux 被应用到移动终端设备的时候,启动慢就成了一个大问题。为了更快地启动,人们开始改进 sysvinit,先后出现了 upstart 和 systemd 这两个主要的新一代 init 系统。Upstart 已经开发了 8 年多,在不少系统中已经替换 sysvinit。Systemd 出现较晚,但发展更快,大有取代 upstart 的趋势。

Linux:浅析 Linux 初始化 init 系统: sysvinit
Linux:浅析 Linux 初始化 init 系统: sysvinit

本文的第一部分先简要介绍 sysvinit。

Sysvinit 概况

sysvinit 就是 system V 风格的 init 系统,顾名思义,它源于 System V 系列 UNIX。它提供了比 BSD 风格 init 系统更高的灵活性。是已经风行了几十年的 UNIX init 系统,一直被各类 Linux 发行版所采用。

运行级别

Sysvinit 用术语 runlevel 来定义”预订的运行模式”。Sysvinit 检查 ‘/etc/inittab’ 文件中是否含有 ‘initdefault’ 项。 这告诉 init 系统是否有一个默认运行模式。如果没有默认的运行模式,那么用户将进入系统控制台,手动决定进入何种运行模式。

sysvinit 中运行模式描述了系统各种预订的运行模式。通常会有 8 种运行模式,即运行模式 0 到 6 和 S 或者 s。

每种 Linux 发行版对运行模式的定义都不太一样。但 0,1,6 却得到了大家的一致赞同:

  • 0 关机
  • 1 单用户模式
  • 6 重启

通常在 /etc/inittab 文件中定义了各种运行模式的工作范围。比如 RedHat 定义了 runlevel 3 和 5。运行模式 3 将系统初始化为字符界面的 shell 模式;运行模式 5 将系统初始化为 GUI 模式。无论是命令行界面还是 GUI,运行模式 3 和 5 相对于其他运行模式而言都是完整的正式的运行状态,计算机可以完成用户需要的任务。而模式 1,S 等往往用于系统故障之后的排错和恢复。

很显然,这些不同的运行模式下系统需要初始化运行的进程和需要进行的初始化准备都是不同的。比如运行模式 3 不需要启动 X 系统。用户只需要指定需要进入哪种模式,sysvinit 将负责执行所有该模式所必须的初始化工作。

sysvinit 运行顺序

Sysvinit 巧妙地用脚本,文件命名规则和软链接来实现不同的 runlevel。首先,sysvinit 需要读取/etc/inittab 文件。分析这个文件的内容,它获得以下一些配置信息:

  • 系统需要进入的 runlevel
  • 捕获组合键的定义
  • 定义电源 fail/restore 脚本
  • 启动 getty 和虚拟控制台

得到配置信息后,sysvinit 顺序地执行以下这些步骤,从而将系统初始化为预订的 runlevel X。

  • /etc/rc.d/rc.sysinit
  • /etc/rc.d/rc 和/etc/rc.d/rcX.d/ (X 代表运行级别 0-6)
  • /etc/rc.d/rc.local
  • X Display Manager(如果需要的话)

首先,运行 rc.sysinit 以便执行一些重要的系统初始化任务。在 RedHat 公司的 RHEL5 中(RHEL6 已经使用 upstart 了),rc.sysinit 主要完成以下这些工作。

  • 激活 udev 和 selinux
  • 设置定义在/etc/sysctl.conf 中的内核参数
  • 设置系统时钟
  • 加载 keymaps
  • 使能交换分区
  • 设置主机名(hostname)
  • 根分区检查和 remount
  • 激活 RAID 和 LVM 设备
  • 开启磁盘配额
  • 检查并挂载所有文件系统
  • 清除过期的 locks 和 PID 文件

完成了以上这些工作之后,sysvinit 开始运行/etc/rc.d/rc 脚本。根据不同的 runlevel,rc 脚本将打开对应该 runlevel 的 rcX.d 目录(X 就是 runlevel),找到并运行存放在该目录下的所有启动脚本。每个 runlevel X 都有一个这样的目录,目录名为/etc/rc.d/rcX.d。

在这些目录下存放着很多不同的脚本。文件名以 S 开头的脚本就是启动时应该运行的脚本,S 后面跟的数字定义了这些脚本的执行顺序。在/etc/rc.d/rcX.d 目录下的脚本其实都是一些软链接文件,真实的脚本文件存放在/etc/init.d 目录下。如下所示:

清单 1.rc5.d 目录下的脚本
[root@www ~]# ll /etc/rc5.d/
lrwxrwxrwx 1 root root 16 Sep  4  2008 K02dhcdbd -> ../init.d/dhcdbd
....(中间省略)....
lrwxrwxrwx 1 root root 14 Sep  4  2008 K91capi -> ../init.d/capi
lrwxrwxrwx 1 root root 23 Sep  4  2008 S00microcode_ctl -> ../init.d/microcode_ctl
lrwxrwxrwx 1 root root 22 Sep  4  2008 S02lvm2-monitor -> ../init.d/lvm2-monitor
....(中间省略)....
lrwxrwxrwx 1 root root 17 Sep  4  2008 S10network -> ../init.d/network
....(中间省略)....
lrwxrwxrwx 1 root root 11 Sep  4  2008 S99local -> ../rc.local
lrwxrwxrwx 1 root root 16 Sep  4  2008 S99smartd -> ../init.d/smartd
....(底下省略)....

当所有的初始化脚本执行完毕。Sysvinit 运行/etc/rc.d/rc.local 脚本。

rc.local 是 Linux 留给用户进行个性化设置的地方。您可以把自己私人想设置和启动的东西放到这里,一台 Linux Server 的用户一般不止一个,所以才有这样的考虑。

Sysvinit 和系统关闭

Sysvinit 不仅需要负责初始化系统,还需要负责关闭系统。在系统关闭时,为了保证数据的一致性,需要小心地按顺序进行结束和清理工作。

比如应该先停止对文件系统有读写操作的服务,然后再 umount 文件系统。否则数据就会丢失。

这种顺序的控制这也是依靠/etc/rc.d/rcX.d/目录下所有脚本的命名规则来控制的,在该目录下所有以 K 开头的脚本都将在关闭系统时调用,字母 K 之后的数字定义了它们的执行顺序。

这些脚本负责安全地停止服务或者其他的关闭工作。

Sysvinit 的管理和控制功能

此外,在系统启动之后,管理员还需要对已经启动的进程进行管理和控制。原始的 sysvinit 软件包包含了一系列的控制启动,运行和关闭所有其他程序的工具。

halt

停止系统。

init

这个就是 sysvinit 本身的 init 进程实体,以 pid1 身份运行,是所有用户进程的父进程。最主要的作用是在启动过程中使用/etc/inittab 文件创建进程。

killall5

就是 SystemV 的 killall 命令。向除自己的会话(session)进程之外的其它进程发出信号,所以不能杀死当前使用的 shell。

last

回溯/var/log/wtmp 文件(或者-f 选项指定的文件),显示自从这个文件建立以来,所有用户的登录情况。

lastb

作用和 last 差不多,默认情况下使用/var/log/btmp 文件,显示所有失败登录企图。

mesg

控制其它用户对用户终端的访问。

pidof

找出程序的进程识别号(pid),输出到标准输出设备。

poweroff

等于 shutdown -h –p,或者 telinit 0。关闭系统并切断电源。

reboot

等于 shutdown –r 或者 telinit 6。重启系统。

runlevel

读取系统的登录记录文件(一般是/var/run/utmp)把以前和当前的系统运行级输出到标准输出设备。

shutdown

以一种安全的方式终止系统,所有正在登录的用户都会收到系统将要终止通知,并且不准新的登录。

sulogin

当系统进入单用户模式时,被 init 调用。当接收到启动加载程序传递的-b 选项时,init 也会调用 sulogin。

telinit

实际是 init 的一个连接,用来向 init 传送单字符参数和信号。

utmpdump

以一种用户友好的格式向标准输出设备显示/var/run/utmp 文件的内容。

wall

向所有有信息权限的登录用户发送消息。

不同的 Linux 发行版在这些 sysvinit 的基本工具基础上又开发了一些辅助工具用来简化 init 系统的管理工作。比如 RedHat 的 RHEL 在 sysvinit 的基础上开发了 initscripts 软件包,包含了大量的启动脚本 (如 rc.sysinit) ,还提供了 service,chkconfig 等命令行工具,甚至一套图形化界面来管理 init 系统。其他的 Linux 发行版也有各自的 initscript 或其他名字的 init 软件包来简化 sysvinit 的管理。

只要您理解了 sysvinit 的机制,在一个最简的仅有 sysvinit 的系统下,您也可以直接调用脚本启动和停止服务,手动创建 inittab 和创建软连接来完成这些任务。因此理解 sysvinit 的基本原理和命令是最重要的。您甚至也可以开发自己的一套管理工具。

Sysvinit 的小结

Sysvinit 的优点是概念简单。Service 开发人员只需要编写启动和停止脚本,概念非常清楚;将 service 添加/删除到某个 runlevel 时,只需要执行一些创建/删除软连接文件的基本操作;这些都不需要学习额外的知识或特殊的定义语法(UpStart 和 Systemd 都需要用户学习新的定义系统初始化行为的语言)。

其次,sysvinit 的另一个重要优点是确定的执行顺序:脚本严格按照启动数字的大小顺序执行,一个执行完毕再执行下一个,这非常有益于错误排查。UpStart 和 systemd 支持并发启动,导致没有人可以确定地了解具体的启动顺序,排错不易。

但是串行地执行脚本导致 sysvinit 运行效率较慢,在新的 IT 环境下,启动快慢成为一个重要问题。此外动态设备加载等 Linux 新特性也暴露出 sysvinit 设计的一些问题。针对这些问题,人们开始想办法改进 sysvinit,以便加快启动时间,并解决 sysvinit 自身的设计问题。

Upstart 是第一个被广泛应用的新一代 init 系统。我们在接下来的第二部分介绍 UpStart。

参考资料

学习

  • Linux From Scratch 项目关于 sysvinit 的材料,对 sysvinit 有比较全面和简练的介绍。
  • 维基百科上 init 系统的条目对 init 进程有简略却精确的介绍。
  • 经典文章鸟哥的 Linux 私房菜中有对 init,rc.d 等概念的精彩而详细的介绍。

来源:http://www.ibm.com/developerworks/cn/linux/1407_liuming_init1/index.html

Linux:使用 FirewallD 构建动态防火墙

FirewallD 提供了支持网络/防火墙区域(zone)定义网络链接以及接口安全等级的动态防火墙管理工具。它支持 IPv4, IPv6 防火墙设置以及以太网桥接,并且拥有运行时配置和永久配置选项。它也支持允许服务或者应用程序直接添加防火墙规则的接口。 以前的 system-config-firewall/lokkit 防火墙模型是静态的,每次修改都要求防火墙完全重启。这个过程包括内核 netfilter 防火墙模块的卸载和新配置所需模块的装载等。而模块的卸载将会破坏状态防火墙和确立的连接。

相反,firewall daemon 动态管理防火墙,不需要重启整个防火墙便可应用更改。因而也就没有必要重载所有内核防火墙模块了。不过,要使用 firewall daemon 就要求防火墙的所有变更都要通过该守护进程来实现,以确保守护进程中的状态和内核里的防火墙是一致的。另外,firewall daemon 无法解析由 ip*tables 和 ebtables 命令行工具添加的防火墙规则。

守护进程通过 D-BUS 提供当前激活的防火墙设置信息,也通过 D-BUS 接受使用 PolicyKit 认证方式做的更改。

Linux:使用 FirewallD 构建动态防火墙
Linux:使用 FirewallD 构建动态防火墙

“守护进程”

应用程序、守护进程和用户可以通过 D-BUS 请求启用一个防火墙特性。特性可以是预定义的防火墙功能,如:服务、端口和协议的组合、端口/数据报转发、伪装、ICMP 拦截或自定义规则等。该功能可以启用确定的一段时间也可以再次停用。

通过所谓的直接接口,其他的服务(例如 libvirt )能够通过 iptables 变元(arguments)和参数(parameters)增加自己的规则。

amanda 、ftp 、samba 和 tftp 服务的 netfilter 防火墙助手也被“守护进程”解决了,只要它们还作为预定义服务的一部分。附加助手的装载不作为当前接口的一部分。由于一些助手只有在由模块控制的所有连接都关闭后才可装载。因而,跟踪连接信息很重要,需要列入考虑范围。

静态防火墙(system-config-firewall/lokkit)

使用 system-config-firewall 和 lokkit 的静态防火墙模型实际上仍然可用并将继续提供,但却不能与“守护进程”同时使用。用户或者管理员可以决定使用哪一种方案。

在软件安装,初次启动或者是首次联网时,将会出现一个选择器。通过它你可以选择要使用的防火墙方案。其他的解决方案将保持完整,可以通过更换模式启用。

firewall daemon 独立于 system-config-firewall,但二者不能同时使用。

使用 iptables 和 ip6tables 的静态防火墙规则

如果你想使用自己的 iptables 和 ip6tables 静态防火墙规则, 那么请安装 iptables-services 并且禁用 firewalld ,启用 iptables 和ip6tables:

yum install iptables-services
systemctl mask firewalld.service
systemctl enable iptables.service
systemctl enable ip6tables.service

静态防火墙规则配置文件是 /etc/sysconfig/iptables 以及 /etc/sysconfig/ip6tables .

注: iptables 与 iptables-services 软件包不提供与服务配套使用的防火墙规则. 这些服务是用来保障兼容性以及供想使用自己防火墙规则的人使用的. 你可以安装并使用 system-config-firewall 来创建上述服务需要的规则. 为了能使用 system-config-firewall, 你必须停止 firewalld.

为服务创建规则并停用 firewalld 后,就可以启用 iptables 与 ip6tables 服务了:

systemctl stop firewalld.service
systemctl start iptables.service
systemctl start ip6tables.service

什么是区域?

网络区域定义了网络连接的可信等级。这是一个一对多的关系,这意味着一次连接可以仅仅是一个区域的一部分,而一个区域可以用于很多连接。

预定义的服务

服务是端口和/或协议入口的组合。备选内容包括 netfilter 助手模块以及 IPv4、IPv6地址。

端口和协议

定义了 tcp 或 udp 端口,端口可以是一个端口或者端口范围。

ICMP 阻塞

可以选择 Internet 控制报文协议的报文。这些报文可以是信息请求亦可是对信息请求或错误条件创建的响应。

伪装

私有网络地址可以被映射到公开的IP地址。这是一次正规的地址转换。

端口转发

端口可以映射到另一个端口以及/或者其他主机。

哪个区域可用?

由firewalld 提供的区域按照从不信任到信任的顺序排序:

丢弃(drop)

任何流入网络的包都被丢弃,不作出任何响应。只允许流出的网络连接。

阻塞(block)

任何进入的网络连接都被拒绝,并返回 IPv4 的 icmp-host-prohibited 报文或者 IPv6 的 icmp6-adm-prohibited 报文。只允许由该系统初始化的网络连接。

公开(public)

用以可以公开的部分。该网络中其他的计算机对你来说不可信并且可能伤害你的计算机。只允许选中的连接接入。

外部(external)

用在路由器等启用伪装的外部网络。该网络中其他的计算机对你来说不可信并且可能伤害你的计算机。只允许选中的连接接入。

隔离区(dmz)

用以允许隔离区(dmz)中的电脑有限地被外界网络访问。只接受被选中的连接。

工作(work)

用在工作网络。该网络中的大多数计算机可以信任,不会影响你的计算机。只接受被选中的连接。

家庭(home)

用在家庭网络。该网络中的大多数计算机可以信任,不会影响你的计算机。只接受被选中的连接。

内部(internal)

用在内部网络。该网络中的大多数计算机可以信任,不会影响你的计算机。只接受被选中的连接。

受信任的(trusted)

允许所有网络连接。

我应该选用哪个区域?

例如,公共的 WIFI 连接应该主要为不受信任的,家庭的有线网络应该是相当可信任的。根据与你使用的网络最符合的区域进行选择。

如何配置或者增加区域?

你可以使用任何一种 firewalld 配置工具来配置或者增加区域,以及修改配置。工具有例如 firewall-config 这样的图形界面工具, firewall-cmd 这样的命令行工具,以及D-BUS接口。或者你也可以在配置文件目录中创建或者拷贝区域文件。 @PREFIX@/lib/firewalld/zones 被用于默认和备用配置,/etc/firewalld/zones 被用于用户创建和自定义配置文件。

如何为网络连接设置或者修改区域

区域设置以 ZONE= 选项 存储在网络连接的ifcfg文件中。如果这个选项缺失或者为空,firewalld 将使用配置的默认区域。

如果这个连接受到 NetworkManager 控制,你也可以使用 nm-connection-editor 来修改区域。

由 NetworkManager 控制的网络连接

防火墙不能够通过 NetworkManager 显示的名称来配置网络连接,只能配置网络接口。因此在网络连接之前 NetworkManager 将配置文件所述连接对应的网络接口告诉 firewalld 。如果在配置文件中没有配置区域,接口将配置到 firewalld 的默认区域。如果网络连接使用了不止一个接口,所有的接口都会应用到 fiwewalld。接口名称的改变也将由 NetworkManager 控制并应用到firewalld。

为了简化,自此,网络连接将被用作与区域的关系。

如果一个接口断开了, NetworkManager 也将告诉 firewalld 从区域中删除该接口。

当 firewalld 由 systemd 或者 init 脚本启动或者重启后,firewalld 将通知 NetworkManager 把网络连接增加到区域。

由脚本控制的网络

对于由网络脚本控制的连接有一条限制:没有守护进程通知 firewalld 将连接增加到区域。这项工作仅在 ifcfg-post 脚本进行。因此,此后对网络连接的重命名将不能被应用到firewalld。同样,在连接活动时重启 firewalld 将导致与其失去关联。现在有意修复此情况。最简单的是将全部未配置连接加入默认区域。

区域定义了本区域中防火墙的特性。

来源:https://fedoraproject.org/wiki/FirewallD/zh-cn#.E7.94.B1.E8.84.9A.E6.9C.AC.E6.8E.A7.E5.88.B6.E7.9A.84.E7.BD.91.E7.BB.9C

Linux:浅析 Linux 初始化 init 系统: UpStart

Upstart 简介

假如您使用的 Linux 发行版是 Ubuntu,很可能会发现在您的计算机上找不到/etc/inittab 文件了,这是因为 Ubuntu 使用了一种被称为 upstart 的新型 init 系统。

Linux:浅析 Linux 初始化 init 系统: UpStart
Linux:浅析 Linux 初始化 init 系统: UpStart

开发 Upstart 的缘由

大约在 2006 年或者更早的时候, Ubuntu 开发人员试图将 Linux 安装在笔记本电脑上。在这期间技术人员发现经典的 sysvinit 存在一些问题:它不适合笔记本环境。这促使程序员 Scott James Remnant 着手开发 upstart。

当 Linux 内核进入 2.6 时代时,内核功能有了很多新的更新。新特性使得 Linux 不仅是一款优秀的服务器操作系统,也可以被用于桌面系统,甚至嵌入式设备。桌面系统或便携式设备的一个特点是经常重启,而且要频繁地使用硬件热插拔技术。在现代计算机系统中,硬件繁多、接口有限,人们并非将所有设备都始终连接在计算机上,比如 U 盘平时并不连接电脑,使用时才插入 USB 插口。因此,当系统上电启动时,一些外设可能并没有连接。而是在启动后当需要的时候才连接这些设备。在 2.6 内核支持下,一旦新外设连接到系统,内核便可以自动实时地发现它们,并初始化这些设备,进而使用它们。这为便携式设备用户提供了很大的灵活性。

可是这些特性为 sysvinit 带来了一些挑战。当系统初始化时,需要被初始化的设备并没有连接到系统上;比如打印机。为了管理打印任务,系统需要启动 CUPS 等服务,而如果打印机没有接入系统的情况下,启动这些服务就是一种浪费。Sysvinit 没有办法处理这类需求,它必须一次性把所有可能用到的服务都启动起来,即使打印机并没有连接到系统,CUPS 服务也必须启动。

还有网络共享盘的挂载问题。在/etc/fstab 中,可以指定系统自动挂载一个网络盘,比如 NFS,或者 iSCSI 设备。在本文的第一部分 sysvinit 的简介中可以看到,sysvinit 分析/etc/fstab 挂载文件系统这个步骤是在网络启动之前。可是如果网络没有启动,NFS 或者 iSCSI 都不可访问,当然也无法进行挂载操作。Sysvinit 采用 netdev 的方式来解决这个问题,即/etc/fstab 发现 netdev 属性挂载点的时候,不尝试挂载它,在网络初始化并使能之后,还有一个专门的 netfs 服务来挂载所有这些网络盘。这是一个不得已的补救方法,给管理员带来不便。部分新手管理员甚至从来也没有听说过 netdev 选项,因此经常成为系统管理的一个陷阱。

针对以上种种情况,Ubuntu 开发人员在评估了当时的几个可选 init 系统之后,决定重新设计和开发一个全新的 init 系统,即 UpStart。UpStart 基于事件机制,比如 U 盘插入 USB 接口后,udev 得到内核通知,发现该设备,这就是一个新的事件。UpStart 在感知到该事件之后触发相应的等待任务,比如处理/etc/fstab 中存在的挂载点。采用这种事件驱动的模式,upstart 完美地解决了即插即用设备带来的新问题。

此外,采用事件驱动机制也带来了一些其它有益的变化,比如加快了系统启动时间。sysvinit 运行时是同步阻塞的。一个脚本运行的时候,后续脚本必须等待。这意味着所有的初始化步骤都是串行执行的,而实际上很多服务彼此并不相关,完全可以并行启动,从而减小系统的启动时间。在 Linux 大量应用于服务器的时代,系统启动时间也许还不那么重要;然而对于桌面系统和便携式设备,启动时间的长短对用户体验影响很大。此外云计算等新的 Server 端技术也往往需要单个设备可以更加快速地启动。

UpStart 满足了这些需求,目前不仅桌面系统 Ubuntu 采用了 UpStart,甚至企业级服务器级的 RHEL 也默认采用 UpStart 来替换 sysvinit 作为 init 系统。

Upstart 的特点

UpStart 解决了之前提到的 sysvinit 的缺点。采用事件驱动模型,UpStart 可以:

  • 更快地启动系统
  • 当新硬件被发现时动态启动服务
  • 硬件被拔除时动态停止服务

这些特点使得 UpStart 可以很好地应用在桌面或者便携式系统中,处理这些系统中的动态硬件插拔特性。

Upstart 概念和术语

Upstart 的基本概念和设计清晰明确。UpStart 主要的概念是 job 和 event。Job 就是一个工作单元,用来完成一件工作,比如启动一个后台服务,或者运行一个配置命令。每个 Job 都等待一个或多个事件,一旦事件发生,upstart 就触发该 job 完成相应的工作。

Job

Job 就是一个工作的单元,一个任务或者一个服务。可以理解为 sysvinit 中的一个服务脚本。有三种类型的工作:

  • task job;
  • service job;
  • abstract job;

task job 代表在一定时间内会执行完毕的任务,比如删除一个文件;

service job 代表后台服务进程,比如 apache httpd。这里进程一般不会退出,一旦开始运行就成为一个后台精灵进程,由 init 进程管理,如果这类进程退出,由 init 进程重新启动,它们只能由 init 进程发送信号停止。它们的停止一般也是由于所依赖的停止事件而触发的,不过 upstart 也提供命令行工具,让管理人员手动停止某个服务;

Abstract job 仅由 upstart 内部使用,仅对理解 upstart 内部机理有所帮助。我们不用关心它。

除了以上的分类之外,还有另一种工作(Job)分类方法。Upstart 不仅可以用来为整个系统的初始化服务,也可以为每个用户会话(session)的初始化服务。系统的初始化任务就叫做 system job,比如挂载文件系统的任务就是一个 system job;用户会话的初始化服务就叫做 session job。

Job 生命周期

Upstart 为每个工作都维护一个生命周期。一般来说,工作有开始,运行和结束这几种状态。为了更精细地描述工作的变化,Upstart 还引入了一些其它的状态。比如开始就有开始之前(pre-start),即将开始(starting)和已经开始了(started)几种不同的状态,这样可以更加精确地描述工作的当前状态。

工作从某种初始状态开始,逐渐变化,或许要经历其它几种不同的状态,最终进入另外一种状态,形成一个状态机。在这个过程中,当工作的状态即将发生变化的时候,init 进程会发出相应的事件(event)。

表 1.Upstart 中 Job 的可能状态
状态名 含义
Waiting 初始状态
Starting Job 即将开始
pre-start 执行 pre-start 段,即任务开始前应该完成的工作
Spawned 准备执行 script 或者 exec 段
post-start 执行 post-start 动作
Running interim state set after post-start section processed denoting job is running (But it may have no associated PID!)
pre-stop 执行 pre-stop 段
Stopping interim state set after pre-stop section processed
Killed 任务即将被停止
post-stop 执行 post-stop 段

图 1 展示了 Job 的状态机。

图 1. Job’s life cycle
Linux:浅析 Linux 初始化 init 系统: UpStart
Linux:浅析 Linux 初始化 init 系统: UpStart

其中有四个状态会引起 init 进程发送相应的事件,表明该工作的相应变化:

  • Starting
  • Started
  • Stopping
  • Stopped

而其它的状态变化不会发出事件。那么我们接下来就来看看事件的详细含义吧。

事件 Event

顾名思义,Event 就是一个事件。事件在 upstart 中以通知消息的形式具体存在。一旦某个事件发生了,Upstart 就向整个系统发送一个消息。没有任何手段阻止事件消息被 upstart 的其它部分知晓,也就是说,事件一旦发生,整个 upstart 系统中所有工作和其它的事件都会得到通知。

Event 可以分为三类: signal,methods 或者 hooks。

Signals

Signal 事件是非阻塞的,异步的。发送一个信号之后控制权立即返回。

Methods

Methods 事件是阻塞的,同步的。

Hooks

Hooks 事件是阻塞的,同步的。它介于 Signals 和 Methods 之间,调用发出 Hooks 事件的进程必须等待事件完成才可以得到控制权,但不检查事件是否成功。

事件是个非常抽象的概念,下面我罗列出一些常见的事件,希望可以帮助您进一步了解事件的含义:

  • 系统上电启动,init 进程会发送”start”事件
  • 根文件系统可写时,相应 job 会发送文件系统就绪的事件
  • 一个块设备被发现并初始化完成,发送相应的事件
  • 某个文件系统被挂载,发送相应的事件
  • 类似 atd 和 cron,可以在某个时间点,或者周期的时间点发送事件
  • 另外一个 job 开始或结束时,发送相应的事件
  • 一个磁盘文件被修改时,可以发出相应的事件
  • 一个网络设备被发现时,可以发出相应的事件
  • 缺省路由被添加或删除时,可以发出相应的事件

不同的 Linux 发行版对 upstart 有不同的定制和实现,实现和支持的事件也有所不同,可以用man 7 upstart-events来查看事件列表。

Job 和 Event 的相互协作

Upstart 就是由事件触发工作运行的一个系统,每一个程序的运行都由其依赖的事件发生而触发的。

系统初始化的过程是在工作和事件的相互协作下完成的,可以大致描述如下:系统初始化时,init 进程开始运行,init 进程自身会发出不同的事件,这些最初的事件会触发一些工作运行。每个工作运行过程中会释放不同的事件,这些事件又将触发新的工作运行。如此反复,直到整个系统正常运行起来。

究竟哪些事件会触发某个工作的运行?这是由工作配置文件定义的。

工作配置文件

任何一个工作都是由一个工作配置文件(Job Configuration File)定义的。这个文件是一个文本文件,包含一个或者多个小节(stanza)。每个小节是一个完整的定义模块,定义了工作的一个方面,比如 author 小节定义了工作的作者。工作配置文件存放在/etc/init 下面,是以.conf 作为文件后缀的文件。

清单 1. 一个最简单的工作配置文件
#This is a simple demo of Job Configure file
#This line is comment, start with #
#Stanza 1, The author
author “Liu Ming”
#Stanza 2, Description
description “This job only has author and description, so no use, just a demo”

上面的例子不会产生任何作用,一个真正的工作配置文件会包含很多小节,其中比较重要的小节有以下几个:

“expect” Stanza

Upstart 除了负责系统的启动过程之外,和 SysVinit 一样,Upstart 还提供一系列的管理工具。当系统启动之后,管理员可能还需要进行维护和调整,比如启动或者停止某项系统服务。或者将系统切换到其它的工作状态,比如改变运行级别。本文后续将详细介绍 Upstart 的管理工具的使用。

为了启动,停止,重启和查询某个系统服务。Upstart 需要跟踪该服务所对应的进程。比如 httpd 服务的进程 PID 为 1000。当用户需要查询 httpd 服务是否正常运行时,Upstart 就可以利用 ps 命令查询进程 1000,假如它还在正常运行,则表明服务正常。当用户需要停止 httpd 服务时,Upstart 就使用 kill 命令终止该进程。为此,Upstart 必须跟踪服务进程的进程号。

部分服务进程为了将自己变成后台精灵进程(daemon),会采用两次派生(fork)的技术,另外一些服务则不会这样做。假如一个服务派生了两次,那么 UpStart 必须采用第二个派生出来的进程号作为服务的 PID。但是,UpStart 本身无法判断服务进程是否会派生两次,为此在定义该服务的工作配置文件中必须写明 expect 小节,告诉 UpStart 进程是否会派生两次。

Expect 有两种,”expect fork”表示进程只会 fork 一次;”expect daemonize”表示进程会 fork 两次。

“exec” Stanza 和”script” Stanza

一个 UpStart 工作一定需要做些什么,可能是运行一条 shell 命令,或者运行一段脚本。用”exec”关键字配置工作需要运行的命令;用”script”关键字定义需要运行的脚本。

清单 2 显示了 exec 和 script 的用法:

清单 2.script 例子
# mountall.conf
description “Mount filesystems on boot”
start on startup
stop on starting rcS
...
script
  . /etc/default/rcS
  [ -f /forcefsck ] && force_fsck=”--force-fsck”
  [ “$FSCKFIX”=”yes” ] && fsck_fix=”--fsck-fix”
  ...
  exec mountall –daemon $force_fsck $fsck_fix
end script
...

这是 mountall 的例子,该工作在系统启动时运行,负责挂载所有的文件系统。该工作需要执行复杂的脚本,由”script”关键字定义;在脚本中,使用了 exec 来执行 mountall 命令。

“start on” Stanza 和”stop on” Stanza

“start on”定义了触发工作的所有事件。”start on”的语法很简单,如下所示:

start on EVENT [[KEY=]VALUE]… [and|or…]

EVENT 表示事件的名字,可以在 start on 中指定多个事件,表示该工作的开始需要依赖多个事件发生。多个事件之间可以用 and 或者 or 组合,”表示全部都必须发生”或者”其中之一发生即可”等不同的依赖条件。除了事件发生之外,工作的启动还可以依赖特定的条件,因此在 start on 的 EVENT 之后,可以用 KEY=VALUE 来表示额外的条件,一般是某个环境变量(KEY)和特定值(VALUE)进行比较。如果只有一个变量,或者变量的顺序已知,则 KEY 可以省略。

“stop on”和”start on”非常类似,只不过是定义工作在什么情况下需要停止。

代码清单 3 是”start on”和”stop on”的一个例子。

清单 3. start on/ stop on 例子
#dbus.conf
description     “D-Bus system message bus”
start on local-filesystems
stop on deconfiguring-networking
…

D-Bus 是一个系统消息服务,上面的配置文件表明当系统发出 local-filesystems 事件时启动 D-Bus;当系统发出 deconfiguring-networking 事件时,停止 D-Bus 服务。

Session Init

UpStart 还可以用于管理用户会话的初始化。在我写这篇文章的今天,多数 Linux 发行版还没有使用 UpStart 管理会话。只有在 Ubuntu Raring 版本中,使用 UpStart 管理用户会话的初始化过程。

首先让我们了解一下 Session 的概念。Session 就是一个用户会话,即用户从远程或者本地登入系统开始工作,直到用户退出。这整个过程就构成一个会话。

每个用户的使用习惯和使用方法都不相同,因此用户往往需要为自己的会话做一个定制,比如添加特定的命令别名,启动特殊的应用程序或者服务,等等。这些工作都属于对特定会话的初始化操作,因此可以被称为 Session Init。

用户使用 Linux 可以有两种模式:字符模式和图形界面。在字符模式下,会话初始化相对简单。用户登录后只能启动一个 Shell,通过 shell 命令使用系统。各种 shell 程序都支持一个自动运行的启动脚本,比如~/.bashrc。用户在这些脚本中加入需要运行的定制化命令。字符会话需求简单,因此这种现有的机制工作的很好。

在图形界面下,事情就变得复杂一些。用户登录后看到的并不是一个 shell 提示符,而是一个桌面。一个完整的桌面环境由很多组件组成。

一个桌面环境包括 window manager,panel 以及其它一些定义在/usr/share/gnome-session/sessions/下面的基本组件;此外还有一些辅助的应用程序,共同帮助构成一个完整的方便的桌面,比如 system monitors,panel applets,NetworkManager,Bluetooth,printers 等。当用户登录之后,这些组件都需要被初始化,这个过程比字符界面要复杂的多。目前启动各种图形组件和应用的工作由 gnome-session 完成。过程如下:

以 Ubuntu 为例,当用户登录 Ubuntu 图形界面后,显示管理器(Display Manager)lightDM 启动 Xsession。Xsession 接着启动 gnome-session,gnome-session 负责其它的初始化工作,然后就开始了一个 desktop session。

图 2.传统 desktop session 启动过程
init
 |- lightdm
 |   |- Xorg
 |   |- lightdm ---session-child
 |        |- gnome-session --session=ubuntu
 |             |- compiz
 |             |- gwibber
 |             |- nautilus
 |             |- nm-applet
 |             :
 |             :
 |
 |- dbus-daemon --session
 |
 :
 :

这个过程有一些缺点(和 sysVInit 类似)。一些应用和组件其实并不需要在会话初始化过程中启动,更好的选择是在需要它们的时候才启动。比如 update-notifier 服务,该服务不停地监测几个文件系统路径,一旦这些路径上发现可以更新的软件包,就提醒用户。这些文件系统路径包括新插入的 DVD 盘等。Update-notifier 由 gnome-session 启动并一直运行着,在多数情况下,用户并不会插入新的 DVD,此时 update-notifier 服务一直在后台运行并消耗系统资源。更好的模式是当用户插入 DVD 的时候再运行 update-notifier。这样可以加快启动时间,减小系统运行过程中的内存等系统资源的开销。对于移动,嵌入式等设备等这还意味着省电。除了 Update-notifier 服务之外,还有其它一些类似的服务。比如 Network Manager,一天之内用户很少切换网络设备,所以大部分时间 Network Manager 服务仅仅是在浪费系统资源;再比如 backup manager 等其它常驻内存,后台不间断运行却很少真正被使用的服务。

用 UpStart 的基于事件的按需启动的模式就可以很好地解决这些问题,比如用户插入网线的时候才启动 Network Manager,因为用户插入网线表明需要使用网络,这可以被称为按需启动。

下图描述了采用 UpStart 之后的会话初始化过程。

图 3.采用 Upstart 的 Desktop session init 过程
init
 |- lightdm
 |   |- Xorg
 |   |- lightdm ---session-child
 |        |- session-init # <-- upstart running as normal user
 |             |- dbus-daemon --session
 |             |- gnome-session --session=ubuntu
 |             |- compiz
 |             |- gwibber
 |             |- nautilus
 |             |- nm-applet
 |             :
 |             :
 :
 :

UpStart 使用

有两种人员需要了解 Upstart 的使用。第一类是系统开发人员,比如 MySQL 的开发人员。它们需要了解如何编写工作配置文件,以便用 UpStart 来管理服务。比如启动,停止 MySQL 服务。

另外一种情况是系统管理员,它们需要掌握 Upstart 的管理命令以便配置和管理系统的初始化,管理系统服务。

系统开发人员需要了解的 UpStart 知识

系统开发人员不仅需要掌握工作配置文件的写法,还需要了解一些针对服务进程编程上的要求。本文仅列出了少数工作配置文件的语法。要全面掌握工作配置文件的写法,需要详细阅读 Upstart 的手册。这里让我们来分析一下如何用 Upstart 来实现传统的运行级别,进而了解如何灵活使用工作配置文件。

Upstart 系统中的运行级别

Upstart 的运作完全是基于工作和事件的。工作的状态变化和运行会引起事件,进而触发其它工作和事件。

而传统的 Linux 系统初始化是基于运行级别的,即 SysVInit。因为历史的原因,Linux 上的多数软件还是采用传统的 SysVInit 脚本启动方式,并没有为 UpStart 开发新的启动脚本,因此即便在 Debian 和 Ubuntu 系统上,还是必须模拟老的 SysVInit 的运行级别模式,以便和多数现有软件兼容。

虽然 Upstart 本身并没有运行级别的概念,但完全可以用 UpStart 的工作模拟出来。让我们完整地考察一下 UpStart 机制下的系统启动过程。

系统启动过程

下图描述了 UpStart 的启动过程。

图 4.UpStart 启动过程
Linux:浅析 Linux 初始化 init 系统: UpStart
Linux:浅析 Linux 初始化 init 系统: UpStart

系统上电后运行 GRUB 载入内核。内核执行硬件初始化和内核自身初始化。在内核初始化的最后,内核将启动 pid 为 1 的 init 进程,即 UpStart 进程。

Upstart 进程在执行了一些自身的初始化工作后,立即发出"startup"事件。上图中用红色方框加红色箭头表示事件,可以在左上方看到"startup"事件。

所有依赖于"startup"事件的工作被触发,其中最重要的是 mountall。mountall 任务负责挂载系统中需要使用的文件系统,完成相应工作后,mountall 任务会发出以下事件:local-filesystem,virtual-filesystem,all-swaps,

其中 virtual-filesystem 事件触发 udev 任务开始工作。任务 udev 触发 upstart-udev-bridge 的工作。Upstart-udev-bridge 会发出 net-device-up IFACE=lo 事件,表示本地回环 IP 网络已经准备就绪。同时,任务 mountall 继续执行,最终会发出 filesystem 事件。

此时,任务 rc-sysinit 会被触发,因为 rc-sysinit 的 start on 条件如下:

start on filesystem and net-device-up IFACE=lo

任务 rc-sysinit 调用 telinit。Telinit 任务会发出 runlevel 事件,触发执行/etc/init/rc.conf。

rc.conf 执行/etc/rc$.d/目录下的所有脚本,和 SysVInit 非常类似,读者可以参考本文第一部分的描述。

程序开发时需要注意的事项

作为程序开发人员,在编写系统服务时,需要了解 UpStart 的一些特殊要求。只有符合这些要求的软件才可以被 UpStart 管理。

规则一,派生次数需声明。

很多 Linux 后台服务都通过派生两次的技巧将自己变成后台服务程序。如果您编写的服务也采用了这个技术,就必须通过文档或其它的某种方式明确地让 UpStart 的维护人员知道这一点,这将影响 UpStart 的 expect stanza,我们在前面已经详细介绍过这个 stanza 的含义。

规则二,派生后即可用。

后台程序在完成第二次派生的时候,必须保证服务已经可用。因为 UpStart 通过派生计数来决定服务是否处于就绪状态。

规则三,遵守 SIGHUP 的要求。

UpStart 会给精灵进程发送 SIGHUP 信号,此时,UpStart 希望该精灵进程做以下这些响应工作:

•完成所有必要的重新初始化工作,比如重新读取配置文件。这是因为 UpStart 的命令"initctl reload"被设计为可以让服务在不重启的情况下更新配置。

•精灵进程必须继续使用现有的 PID,即收到 SIGHUP 时不能调用 fork。如果服务必须在这里调用 fork,则等同于派生两次,参考上面的规则一的处理。这个规则保证了 UpStart 可以继续使用 PID 管理本服务。

规则四,收到 SIGTEM 即 shutdown。

•当收到 SIGTERM 信号后,UpStart 希望精灵进程进程立即干净地退出,释放所有资源。如果一个进程在收到 SIGTERM 信号后不退出,Upstart 将对其发送 SIGKILL 信号。

系统管理员需要了解的 Upstart 命令

作为系统管理员,一个重要的职责就是管理系统服务。比如系统服务的监控,启动,停止和配置。UpStart 提供了一系列的命令来完成这些工作。其中的核心是initctl,这是一个带子命令风格的命令行工具。

比如可以用 initctl list 来查看所有工作的概况:

$initctl list
alsa-mixer-save stop/waiting
avahi-daemon start/running, process 690
mountall-net stop/waiting
rc stop/waiting
rsyslog start/running, process 482
screen-cleanup stop/waiting
tty4 start/running, process 859
udev start/running, process 334
upstart-udev-bridge start/running, process 304
ureadahead-other stop/waiting

这是在 Ubuntu10.10 系统上的输出,其它的 Linux 发行版上的输出会有所不同。第一列是工作名,比如 rsyslog。第二列是工作的目标;第三列是工作的状态。

此外还可以用 initctl stop 停止一个正在运行的工作;用 initctl start 开始一个工作;还可以用 initctl status 来查看一个工作的状态;initctl restart 重启一个工作;initctl reload 可以让一个正在运行的服务重新载入配置文件。这些命令和传统的 service 命令十分相似。

表 2.service 命令和 initctl 命令对照表
Service 命令 UpStart initctl 命令
service start initctl start
service stop initctl stop
service restart initctl restart
service reload initctl reload

很多情况下管理员并不喜欢子命令风格,因为需要手动键入的字符太多。UpStart 还提供了一些快捷命令来简化 initctl,实际上这些命令只是在内部调用相应的 initctl 命令。比如 reload,restart,start,stop 等等。启动一个服务可以简单地调用

start 

这和执行 initctl start 是一样的效果。

一些命令是为了兼容其它系统(主要是 sysvinit),比如显示 runlevel 用/sbin/runlevel 命令:

$runlevel
N 2

这个输出说明当前系统的运行级别为 2。而且系统没有之前的运行级别,也就是说在系统上电启动进入预定运行级别之后没有再修改过运行级别。

那么如何修改系统上电之后的默认运行级别呢?

在 Upstart 系统中,需要修改/etc/init/rc-sysinti.conf 中的 DEFAULT_RUNLEVEL 这个参数,以便修改默认启动运行级别。这一点和 sysvinit 的习惯有所不同,大家需要格外留意。

还有一些随 UpStart 发布的小工具,用来帮助开发 UpStart 或者诊断 UpStart 的问题。比如 init-checkconf 和 upstart-monitor

还可以使用 initctl 的 emit 命令从命令行发送一个事件。

#initctl emit 

这一般是用于 UpStart 本身的排错。

Upstart 小结

可以看到,UpStart 的设计比 SysVInit 更加先进。多数 Linux 发行版上已经不再使用 SysVInit,一部分发行版采用了 UpStart,比如 Ubuntu;而另外一些比如 Fedora,采用了一种被称为 systemd 的 init 系统。Systemd 出现的比 UpStart 更晚,但发展迅速,虽然 UpStart 也还在积极开发并被越来越多地应用,但 systemd 似乎发展更快,我将在下一篇文章中再介绍 systemd。

参考资料

学习

  • Upstart 项目发起人 Scott 的博客文章 upstart-in-universe,有很好的背景描述和实现的概括。值得仔细阅读。
  • LWN 上的文章 Upstart for user sessions 描述了 UpStart 用于 session init 的进展情况,并给出了一些重要的其它参考资料,是学习 Session Init 的好材料。
  • 维基百科上关于 upstart 的介绍,对 upstart 的原理和背景有很好的描述。
  • Ubuntu 维基上关于 upstart 的介绍,介绍了 upstart 项目的背景,use case,基本概念和设计思想,是非常好的参考资料。

来源:http://www.ibm.com/developerworks/cn/linux/1407_liuming_init2/index.html

Linux:浅析 Linux 初始化 init 系统: Systemd

Systemd 的简介和特点

Systemd 是 Linux 系统中最新的初始化系统(init),它主要的设计目标是克服 sysvinit 固有的缺点,提高系统的启动速度。systemd 和 ubuntu 的 upstart 是竞争对手,预计会取代 UpStart,实际上在作者写作本文时,已经有消息称 Ubuntu 也将采用 systemd 作为其标准的系统初始化系统。

Linux:浅析 Linux 初始化 init 系统: Systemd
Linux:浅析 Linux 初始化 init 系统: Systemd

Systemd 的很多概念来源于苹果 Mac OS 操作系统上的 launchd,不过 launchd 专用于苹果系统,因此长期未能获得应有的广泛关注。Systemd 借鉴了很多 launchd 的思想,它的重要特性如下:

同 SysVinit 和 LSB init scripts 兼容

Systemd 是一个”新来的”,Linux 上的很多应用程序并没有来得及为它做相应的改变。和 UpStart 一样,systemd 引入了新的配置方式,对应用程序的开发也有一些新的要求。如果 systemd 想替代目前正在运行的初始化系统,就必须和现有程序兼容。任何一个 Linux 发行版都很难为了采用 systemd 而在短时间内将所有的服务代码都修改一遍。

Systemd 提供了和 Sysvinit 以及 LSB initscripts 兼容的特性。系统中已经存在的服务和进程无需修改。这降低了系统向 systemd 迁移的成本,使得 systemd 替换现有初始化系统成为可能。

更快的启动速度

Systemd 提供了比 UpStart 更激进的并行启动能力,采用了 socket / D-Bus activation 等技术启动服务。一个显而易见的结果就是:更快的启动速度。

为了减少系统启动时间,systemd 的目标是:

  • 尽可能启动更少的进程
  • 尽可能将更多进程并行启动

同样地,UpStart 也试图实现这两个目标。UpStart 采用事件驱动机制,服务可以暂不启动,当需要的时候才通过事件触发其启动,这符合第一个设计目标;此外,不相干的服务可以并行启动,这也实现了第二个目标。

下面的图形演示了 UpStart 相对于 SysVInit 在并发启动这个方面的改进:

图 1. UpStart 对 SysVinit 的改进
Linux:浅析 Linux 初始化 init 系统: Systemd
Linux:浅析 Linux 初始化 init 系统: Systemd

假设有 7 个不同的启动项目, 比如 JobA、Job B 等等。在 SysVInit 中,每一个启动项目都由一个独立的脚本负责,它们由 sysVinit 顺序地,串行地调用。因此总的启动时间为 T1+T2+T3+T4+T5+T6+T7。其中一些任务有依赖关系,比如 A,B,C,D。

而 Job E 和 F 却和 A,B,C,D 无关。这种情况下,UpStart 能够并发地运行任务{E,F,(A,B,C,D)},使得总的启动时间减少为 T1+T2+T3。

这无疑增加了系统启动的并行性,从而提高了系统启动速度。但是在 UpStart 中,有依赖关系的服务还是必须先后启动。比如任务 A,B,(C,D)因为存在依赖关系,所以在这个局部,还是串行执行。

让我们例举一些例子, Avahi 服务需要 D-Bus 提供的功能,因此 Avahi 的启动依赖于 D-Bus,UpStart 中,Avahi 必须等到 D-Bus 启动就绪之后才开始启动。类似的,livirtd 和 X11 都需要 HAL 服务先启动,而所有这些服务都需要 syslog 服务记录日志,因此它们都必须等待 syslog 服务先启动起来。然而 httpd 和他们都没有关系,因此 httpd 可以和 Avahi 等服务并发启动。

Systemd 能够更进一步提高并发性,即便对于那些 UpStart 认为存在相互依赖而必须串行的服务,比如 Avahi 和 D-Bus 也可以并发启动。从而实现如下图所示的并发启动过程:

图 2. systemd 的并发启动

systemd 的并发启动

所有的任务都同时并发执行,总的启动时间被进一步降低为 T1。

可见 systemd 比 UpStart 更进一步提高了并行启动能力,极大地加速了系统启动时间。

systemd 提供按需启动能力

当 sysvinit 系统初始化的时候,它会将所有可能用到的后台服务进程全部启动运行。并且系统必须等待所有的服务都启动就绪之后,才允许用户登录。这种做法有两个缺点:首先是启动时间过长;其次是系统资源浪费。

某些服务很可能在很长一段时间内,甚至整个服务器运行期间都没有被使用过。比如 CUPS,打印服务在多数服务器上很少被真正使用到。您可能没有想到,在很多服务器上 SSHD 也是很少被真正访问到的。花费在启动这些服务上的时间是不必要的;同样,花费在这些服务上的系统资源也是一种浪费。

Systemd 可以提供按需启动的能力,只有在某个服务被真正请求的时候才启动它。当该服务结束,systemd 可以关闭它,等待下次需要时再次启动它。

Systemd 采用 Linux 的 Cgroup 特性跟踪和管理进程的生命周期

init 系统的一个重要职责就是负责跟踪和管理服务进程的生命周期。它不仅可以启动一个服务,也必须也能够停止服务。这看上去没有什么特别的,然而在真正用代码实现的时候,您或许会发现停止服务比一开始想的要困难。

服务进程一般都会作为精灵进程(daemon)在后台运行,为此服务程序有时候会派生(fork)两次。在 UpStart 中,需要在配置文件中正确地配置 expect 小节。这样 UpStart 通过对 fork 系统调用进行计数,从而获知真正的精灵进程的 PID 号。比如图 3 所示的例子:

图 3. 找到正确 pid

找到正确 pid

如果 UpStart 找错了,将 p1`作为服务进程的 Pid,那么停止服务的时候,UpStart 会试图杀死 p1`进程,而真正的 p1“进程则继续执行。换句话说该服务就失去控制了。

还有更加特殊的情况。比如,一个 CGI 程序会派生两次,从而脱离了和 Apache 的父子关系。当 Apache 进程被停止后,该 CGI 程序还在继续运行。而我们希望服务停止后,所有由它所启动的相关进程也被停止。

为了处理这类问题,UpStart 通过 strace 来跟踪 fork、exit 等系统调用,但是这种方法很笨拙,且缺乏可扩展性。systemd 则利用了 Linux 内核的特性即 CGroup 来完成跟踪的任务。当停止服务时,通过查询 CGroup,systemd 可以确保找到所有的相关进程,从而干净地停止服务。

CGroup 已经出现了很久,它主要用来实现系统资源配额管理。CGroup 提供了类似文件系统的接口,使用方便。当进程创建子进程时,子进程会继承父进程的 CGroup。因此无论服务如何启动新的子进程,所有的这些相关进程都会属于同一个 CGroup,systemd 只需要简单地遍历指定的 CGroup 即可正确地找到所有的相关进程,将它们一一停止即可。

启动挂载点和自动挂载的管理

传统的 Linux 系统中,用户可以用/etc/fstab 文件来维护固定的文件系统挂载点。这些挂载点在系统启动过程中被自动挂载,一旦启动过程结束,这些挂载点就会确保存在。这些挂载点都是对系统运行至关重要的文件系统,比如 HOME 目录。和 sysvinit 一样,Systemd 管理这些挂载点,以便能够在系统启动时自动挂载它们。Systemd 还兼容/etc/fstab 文件,您可以继续使用该文件管理挂载点。

有时候用户还需要动态挂载点,比如打算访问 DVD 内容时,才临时执行挂载以便访问其中的内容,而不访问光盘时该挂载点被取消(umount),以便节约资源。传统地,人们依赖 autofs 服务来实现这种功能。

Systemd 内建了自动挂载服务,无需另外安装 autofs 服务,可以直接使用 systemd 提供的自动挂载管理能力来实现 autofs 的功能。

实现事务性依赖关系管理

系统启动过程是由很多的独立工作共同组成的,这些工作之间可能存在依赖关系,比如挂载一个 NFS 文件系统必须依赖网络能够正常工作。Systemd 虽然能够最大限度地并发执行很多有依赖关系的工作,但是类似”挂载 NFS”和”启动网络”这样的工作还是存在天生的先后依赖关系,无法并发执行。对于这些任务,systemd 维护一个”事务一致性”的概念,保证所有相关的服务都可以正常启动而不会出现互相依赖,以至于死锁的情况。

能够对系统进行快照和恢复

systemd 支持按需启动,因此系统的运行状态是动态变化的,人们无法准确地知道系统当前运行了哪些服务。Systemd 快照提供了一种将当前系统运行状态保存并恢复的能力。

比如系统当前正运行服务 A 和 B,可以用 systemd 命令行对当前系统运行状况创建快照。然后将进程 A 停止,或者做其他的任意的对系统的改变,比如启动新的进程 C。在这些改变之后,运行 systemd 的快照恢复命令,就可立即将系统恢复到快照时刻的状态,即只有服务 A,B 在运行。一个可能的应用场景是调试:比如服务器出现一些异常,为了调试用户将当前状态保存为快照,然后可以进行任意的操作,比如停止服务等等。等调试结束,恢复快照即可。

这个快照功能目前在 systemd 中并不完善,似乎开发人员也没有特别关注它,因此有报告指出它还存在一些使用上的问题,使用时尚需慎重。

日志服务

systemd 自带日志服务 journald,该日志服务的设计初衷是克服现有的 syslog 服务的缺点。比如:

  • syslog 不安全,消息的内容无法验证。每一个本地进程都可以声称自己是 Apache PID 4711,而 syslog 也就相信并保存到磁盘上。
  • 数据没有严格的格式,非常随意。自动化的日志分析器需要分析人类语言字符串来识别消息。一方面此类分析困难低效;此外日志格式的变化会导致分析代码需要更新甚至重写。

Systemd Journal 用二进制格式保存所有日志信息,用户使用 journalctl 命令来查看日志信息。无需自己编写复杂脆弱的字符串分析处理程序。

Systemd Journal 的优点如下:

  • 简单性:代码少,依赖少,抽象开销最小。
  • 零维护:日志是除错和监控系统的核心功能,因此它自己不能再产生问题。举例说,自动管理磁盘空间,避免由于日志的不断产生而将磁盘空间耗尽。
  • 移植性:日志 文件应该在所有类型的 Linux 系统上可用,无论它使用的何种 CPU 或者字节序。
  • 性能:添加和浏览 日志 非常快。
  • 最小资源占用:日志 数据文件需要较小。
  • 统一化:各种不同的日志存储技术应该统一起来,将所有的可记录事件保存在同一个数据存储中。所以日志内容的全局上下文都会被保存并且可供日后查询。例如一条固件记录后通常会跟随一条内核记录,最终还会有一条用户态记录。重要的是当保存到硬盘上时这三者之间的关系不会丢失。Syslog 将不同的信息保存到不同的文件中,分析的时候很难确定哪些条目是相关的。
  • 扩展性:日志的适用范围很广,从嵌入式设备到超级计算机集群都可以满足需求。
  • 安全性:日志 文件是可以验证的,让无法检测的修改不再可能。

来源:http://www.ibm.com/developerworks/cn/linux/1407_liuming_init3/index.html

Linux:Linux有问必答——如何在Linux命令行中剪裁图像

问题:我想要去除图像文件中的白色空白,有没有什么便捷的方法能在Linux命令行中对图像文件进行剪裁?

当涉及到在Linux中转换或编辑图像文件时,ImageMagick毫无疑问是最为熟知的一体化软件之一。它包含了一整套命令行工具,用以显示、转换,或复制超过200中类型的光栅或矢量图像文件,所有这一切都在命令行下完成。ImageMagick可以用于多样化的图像编辑工作,如转换文件格式,添加特殊效果,添加文本,以及改变图像(调整大小、旋转、翻转、剪裁)。

如果你想要剪裁映像以去除空白,你可以使用ImageMagick自带的两个命令行工具。如果你还没有安装ImageMagick,请参照本指南来安装。

在本教程中,让我们来剪裁以下PNG图像。我们想要去除图像右边和底部的边缘,以便让图标居中。

首先,鉴定图像文件的尺寸(宽度和高度)。你可以使用identity命令来完成。

 $ identify chart.png

chart.png PNG 1500x1000 1500x1000+0+0 8-bit DirectClass 31.7KB 0.000u 0:00.000

就像上面显示的那样,输入的图像是1500x1000px。

接下来,确定图像剪裁要做的两件事:(1)剪裁图像开始的位置(2)剪裁矩形区域的大小。

在本实例中,让我们假定图像剪裁从左上角开始,更精确点是在x=20px和y=10px,那样的话,剪裁后的图像尺寸为1200x700px。

用于剪裁图像的工具是convert。使用“-crop”选项后,convert命令会在输入图像中剪裁出一个矩形区域。

 $ convert chart.png -crop 1200x700+20+10 chart-cropped.png

指定输入图像为chart.png,convert命令会将剪裁后的图像存储为chart-cropped.png。


via: http://ask.xmodulo.com/crop-image-command-line-linux.html

译者:GOLinux 校对:Caroline

本文由 LCTT 原创翻译,Linux中国 荣誉推出

来源:https://linux.cn/article-4429-1.html

Linux:玩转Docker镜像

前言

Docker是Docker.Inc公司开源的一个基于轻量级虚拟化技术的容器引擎项目,整个项目基于Go语言开发,并遵从Apache 2.0协议。通过分层镜像标准化和内核虚拟化技术,Docker使得应用开发者和运维工程师可以以统一的方式跨平台发布应用,并且以几乎没有额外开销的情况下提供资源隔离的应用运行环境。由于众多新颖的特性以及项目本身的开放性,Docker在不到两年的时间里迅速获得诸多IT厂商的参与,其中更是包括Google、Microsoft、VMware等业界行业领导者。同时,Docker在开发者社区也是一石激起千层浪,许多如我之码农纷纷开始关注、学习和使用Docker,许多企业,尤其是互联网企业,也在不断加大对Docker的投入,大有掀起一场容器革命之势。

Linux:玩转Docker镜像
Linux:玩转Docker镜像

Docker镜像命名解析

镜像是Docker最核心的技术之一,也是应用发布的标准格式。无论你是用docker pull image,或者是在Dockerfile里面写FROM image,从Docker官方Registry下载镜像应该是Docker操作里面最频繁的动作之一了。那么在我们执行docker pull image时背后到底发生了什么呢?在回答这个问题前,我们需要先了解下docker镜像是如何命名的,这也是Docker里面比较容易令人混淆的一块概念:Registry,Repository, Tag and Image。

下面是在本地机器运行docker images的输出结果:

1

我们可以发现我们常说的“ubuntu”镜像其实不是一个镜像名称,而是代表了一个名为ubuntu的Repository,同时在这个Repository下面有一系列打了tag的Image,Image的标记是一个GUID,为了方便也可以通过Repository:tag来引用。

那么Registry又是什么呢?Registry存储镜像数据,并且提供拉取和上传镜像的功能。Registry中镜像是通过Repository来组织的,而每个Repository又包含了若干个Image。

  • Registry包含一个或多个Repository
  • Repository包含一个或多个Image
  • Image用GUID表示,有一个或多个Tag与之关联

那么在哪里指定Registry呢?让我们再拉取一个更完整命名的镜像吧:

2

上面我试图去拉取一个ubuntu镜像,并且指定了Registry为我本机搭建的私有Registry。下面是Docker CLI中pull命令的代码片段 (docker/api/client/command.go中的CmdPull函数)

截图 (4)

在运行时,上面的taglessRemote变量会被传入localhost:5000/ubuntu。上面代码试图从taglessRemote变量中解析出Registry的地址,在我们的例子中,它是localhost:5000。

那我们回过头再来看看下面这个耳熟能详的pull命令背后的故事吧:

3

我们跟着上面的示例代码,进一步进入解析函数ResolveRepositoryName的定义代码片段(docker/registry/registry.go)

Linux:玩转Docker镜像
Linux:玩转Docker镜像

我们发现,Docker CLI会判断传入的taglessRemote参数的第一部分中是否包含’.’或者’:’,如果存在则认为第一部分是Registry地址,否则会使用Docker官方默认的Registry(即index.docker.io其实这里是一个Index Server,和Registry的区别留在后面再去深究吧),即上面代码中高亮的部分。背后的故事还没有结束,如果你向DockerHub上传过镜像,应该记得你上传的镜像名称格式为user-name/repository:tag,这样用户Bob和用户Alice可以有相同名称的Repository,通过用户名前缀作为命名空间隔离,比如Bob/ubuntu和Alice/ubuntu。官方镜像是通过用户名library来区分的,具体代码片段如下(docker/api/client/command.go中的CmdPull函数)

Linux:玩转Docker镜像
Linux:玩转Docker镜像

我们回过头再去看Docker命令行中解析Tag的逻辑吧(docker/api/client/command.go中的CmdPull函数):

截图 (6)

代码会试着在用户输入的Image名称中找’ : ‘后面的tag,如果不存在,会使用默认的‘DEFAULTTAG’,即‘latest’。

也就是说在我们的例子里面,命令会被解析为下面这样(注意,下面的命令不能直接运行,因为Docker CLI不允许明确指定官方Registry地址)

4 (1)

配置Registry Mirror

Docker之所以这么吸引人,除了它的新颖的技术外,围绕官方Registry(Docker Hub)的生态圈也是相当吸引人眼球的地方。在Docker Hub上你可以很轻松下载到大量已经容器化好的应用镜像,即拉即用。这些镜像中,有些是Docker官方维护的,更多的是众多开发者自发上传分享的。而且你还可以在Docker Hub中绑定你的代码托管系统(目前支持Github和Bitbucket)配置自动生成镜像功能,这样Docker Hub会在你代码更新时自动生成对应的Docker镜像,是不是很方便?

不幸的是Docker Hub并没有在国内放服务器或者用国内的CDN,下载个镜像20分钟最起码,我等码农可耗不起这么长时间,老板正站在身后催着我们搬运代码呢。为了克服跨洋网络延迟,一般有两个解决方案:一是使用私有Registry,另外是使用Registry Mirror,我们下面一一展开聊聊.

方案一就是搭建或者使用现有的私有Registry,通过定期和Docker Hub同步热门的镜像,私有Registry上保存了一些镜像的副本,然后大家可以通过docker pull private-registry.com/user-name/ubuntu:latest,从这个私有Registry上拉取镜像。因为这个方案需要定期同步Docker Hub镜像,因此它比较适合于使用的镜像相对稳定,或者都是私有镜像的场景。而且用户需要显式的映射官方镜像名称到私有镜像名称,私有Registry更多被大家应用在企业内部场景。私有Registry部署也很方便,可以直接在Docker Hub上下载Registry镜像,即拉即用,具体部署可以参考官方文档

方案二是使用Registry Mirror,它的原理类似于缓存,如果镜像在Mirror中命中则直接返回给客户端,否则从存放镜像的Registry上拉取并自动缓存在Mirror中。最酷的是,是否使用Mirror对Docker使用者来讲是透明的,也就是说在配置Mirror以后,大家可以仍然输入docker pull ubuntu来拉取Docker Hub镜像,除了速度变快了,和以前没有任何区别。

了以更便捷的方式对接Docker Hub生态圈,使用Registry Mirror自然成为我的首选。接下来我就和大家一起看看Docker使用Mirror来拉取镜像的过程。下面的例子,我使用的是由DaoCloud提供的Registry Mirror服务,在申请开通Mirror服务后你会得到一个Mirror地址,然后我们要做的就是把这个地址配置在Docker Server启动脚本中,重启Docker服务后Mirror配置就生效了(如何获得Mirror服务可以参考本篇文章的附录)

Ubuntu下配置Docker Registry Mirror的命令如下:

sudo echo “DOCKER_OPTS=”$DOCKER_OPTS –registry-mirror=http://your-id.m.daocloud.io -d”” >> /etc/default/dockersudo service docker restart

如果你是用的Boot2Docker,配置命令为:

# 进入Boot2Docker Start Shell,并执行sudo suecho “EXTRA_ARGS=”–registry-mirror=http://your-id.m.daocloud.io”” >> /var/lib/boot2docker/profileexit# 重启Boot2Docker

配置好Registry Mirror后,就可以拉取Docker镜像了,经我测试,使用DaoCloud的Mirror后,拉取常见镜像的速度可以达到1.5M左右,具体速度在你的网络环境可能会略有不同。

我们来看看配置了Registry Mirror后,Docker拉取镜像的过程吧。首先是CLI拉取镜像命令代码片段(docker/api/client/command.go中的CmdPull函数)

Linux:玩转Docker镜像
Linux:玩转Docker镜像

首先,Docker CLI会试图获得授权,在我们的例子中会向https://index.docker.io/v1请求认证,认证完成后,认证服务器会返回一个对应的Token。注意,这里用户认证与配置的Registry Mirror完全无关,这样我们就不用担心使用Mirror的安全问题了。接着Docker CLI会调用Docker Server(即Docker daemon程序)的创建镜像命令,Docker Server随之会执行具体的拉取镜像动作,代码片段如下(docker/graph/pull.go的pullRepository函数)

Linux:玩转Docker镜像
Linux:玩转Docker镜像

从代码中可以发现,如果配置了Registry Mirror,Docker Server会首先从Mirror中拉取镜像,如果Mirror拉取失败会退而求其次从镜像中指定的Registry拉取。大家又可以松口气了,就算配置的Registry Mirror失效,也不会影响用户拉取镜像,只不过速度就。。。

镜像拉下来后,就可以运行容器了

截图 (9)

 

来源:http://blog.daocloud.io/how-to-master-docker-image/

vim: VIM编辑器常用功能整理笔记

vim编辑器

vi : visual Inertface 可视化接口

vim : vi improved 扩展版

语法着色

模式化编辑器 :

编辑模式(命令模式): 默认模式

输入模式:

末行模式:

等待输入命令

10d 删除第十行

10,20d

set nu 显示行号

! ls /etc 不用退出文件输入 系统shell命令

模式转换

编辑–> 输入

小写

i:在当前光标位置前面输入

a:在当前光标位置后面输入

o:在当前光标下一行新建一行 输入内容

大写

I 行首

A 行尾

O 上一行新建一行

输入–> 编辑   ESC

编辑–>末行: 必须得从编辑模式进入 只需要输入:冒号

末行–> 编辑: esc esc

vim: VIM编辑器常用功能整理笔记
vim: VIM编辑器常用功能整理笔记

功能介绍:

一、 打开文件

vim +12 光标到12行

vim + 打开光标到末尾

vim +/PATERN 打开文件定位到第一次匹配的位置

二、关闭文件

末行模式关闭文件 : wq ==:x

编辑模式关闭文件 : 大写ZZ 保存退出 就是 shift+zz

三、移动光标(编辑模式 )

1. 逐个字符移动 (手的位置)

h

l

j 下

k 上

8l 向左移动8个字符

2.逐单词移动

w 移到下一个单词的词首

e 移到当前或者下一个单词的词尾

b 移到当前或者前一个单词的词尾

3w 向后跳3个单词

3.行内跳转

0(零) 绝对行首

^ 行首的第一个非空白字符

$ 绝对行尾

4.行间跳转

#G 跳转至第#行

G 跳转到最后一行

末行模式下: 直接给行号回车

四、翻屏

crtl+f:向下翻一屏

crtl+b:向上翻一屏

crtl+d:向下翻半屏

crtl+u:向上翻半屏

五、删除单个字符

#x 删除 光标所在处后面#个字符 x删除单个

六、删除命令:d

d命令和跳转命令组合使用 3dw

#dw, #de #db

dd: 删除当前光标所在行 3dd

#dd 删除包括当前光标所在行在内的#行

末行模式下:

starAdd,EndAdd . , +5 表示删除当前行到后面五行

支持相对表示法:

. 当前行

$ 最后行 $-3

+# 向下的#行

七、粘贴命令:p

删除的东西不会立即删除,删除的内容保存在缓冲区中

最后一次删除的内容 可以粘贴到指定地区 可以粘贴n次

小p: 如果删除或复制为整行,则粘贴至所在行的下方;非整行,则粘贴至所在行的下方

大P: 相反

八、复制命令 y

用法同d

九、先删除内容,再转换为输入模式

修改

c: 用法同d

十、替换单个字符

r

R 替换模式

十一、撤销编辑undo

u:撤销前一次操作 连续使用

只能在缓存中保存50次

3u 至倒数第三次

后悔撤销操作了,就有 Crtl+r 撤销的撤销 ,还原最近一次的撤销操作

类似于window下的crtl+z Crtl+y

十二、重复前一次编辑操作

点命令 .

十三、可视化操作

v:按字符选取

V: 矩形选取(整行选中)

十四、查找

/PATTERN 往后查找

?PATTERN 向前查找

n 下一个

N 上一个

十五、查找并替换

用法同set

在末行模式下使用s命令

startadd,endadd @ pattern @string @gi

: .,$-1 s/hello/HELLO @g

1,$ == % 表示全文

十六、使用vim打开多个文件

多个文件间复制粘贴

vim file1 file2 file3

:next 切换至前一个文件

:prev 切换至前一个文件

:last 切换至最后一个

:first切换至第一个文件

:qa 全部退出 q只能退出当前

十七、分屏显示一个文件 (word里面的窗口拆分)

Crtl+w,s :水平拆分窗口

Crtl+w,v :垂直拆分窗口

在窗口间切换

Crtl+w,APROW(方向键) 或者两次Crtl+w也能向下一个切换

:qa 关闭所有窗口

十八、分窗口多个文件

vim -o file1 file2 水平分割

vim -O file1 file2 垂直分割

切换 也是 crtl+w

十九、将当前文件中部分内容另存为另外一个文件

末行模式下使用w命令

:w

:addr1,addr2 w /path/to/somewhere

二十、将 另外一个文件的内容填充在当前文件中

:r /root/inittal

直接在后面添加另一个文件的内容

二十一、和shell交互

:!commen

然后回车后 会再返回到vi编辑界面

二十二、高级话题

1、显示或取消行号

:set nu

:set nonu

2.显示忽略或区分字符大小写 :set ignorecase

:set ic

: set noic

3.设定自动缩进 :set autoindent

写脚本编程的时候很重要

:set ai

:set noai

4、查找到的文本高亮显示 或者取消掉

:set hlsearch

:set nohlsearch

5.语法高亮

:syntax on

:syntax off

二十三、配置文件

/etc/vimrc

单用户家目录下 自己新建.vimrc

~/.vimrc

vim 进程强行被退出 网络断开的时候 经常会报错

vim -r inittab

file.swp 文件要手动删掉 备份文件 rm -f .inittab.swp

If this is the case, use “:recover>” or “vim -r inittab>”

to recover the changes (see “:help recovery>”).

If you did this already, delete the swap file “.inittab.swp>”

原文:http://www.cnblogs.com/manue1/p/4478330.html

vim: Vim 配置、插件和使用技巧

vim: Vim 配置、插件和使用技巧
vim: Vim 配置、插件和使用技巧

vim_cheat_sheet_for_programmers.png

常言道:工欲善其事,必先利其器 ,作为一个程序员,一个常用的工具就是 编辑器,我选择一个能极大提高自己开发效率的编辑器 vim(有些人可能选择 emacs)。而 vim编辑器方面具有以下几种特性:

  • 跨平台及统一环境无论是在windows还是在*nix,vim是一个很完美的跨平台文本编辑器,甚至可以直接在服务器平台CentOS,Ubuntu等直接配置使用,配置文件大同小异,操作习惯基本相同。

  • 定制化及可扩展

    vim提供一个 vimrc的配置文件来配置vim,并且自己可以定制一些插件来实现文件浏览( NERD Tree),代码补全( YouCompleteMe,语法检查( syntastic),文件模糊搜索( ctrlp),显示vim状态栏( Vim Powerline),主题颜色( Molokai),显示文件结构( tagbar)等多种功能。

  • 高效命令行使用vim编辑文本,只需在键盘上操作就可以,根本无需用到鼠标。就拿光标移动来说,与重复击键、一个字符一个字符或一行一行移动相比,按一次键就能以词、行、块或函数为单位移动,效率高得多。有时一些重复删除、粘帖的操作,也只需一条命令就可以完成,甚至你可以用键映射来简化或组合多种命令来提高效率。

配置

如果你需要配置vim,只需在Home目录创建一个 ~/.vimrc文件即可以配置vim了,可以参考我的 vimrc配置文件。由于我需要安装插件,并且将需要安装的插件列表分离到另外一个文件 ~/.vimrc.bundles,这个文件也是存放在Home目录,文件内容可以参考 vimrc.bundles。若想加载 ~/.vimrc.bundles文件,必须在 ~/.vimrc文件加入以下代码片段:

if filereadable(expand("~/.vimrc.bundles>"))
  source ~/.vimrc.bundles
endif 

插件

插件管理工具vunble

vundle是vim的插件管理工具,它能够搜索、安装、更新和移除vim插件,再也不需要手动管理vim插件。

  1. 在 Home目录创建 ~/.vim目录和 .vimrc文件(可复制我的 vimrc文件)
  2. 安装vundle

    git clone https://github.com/gmarik/vundle.git ~/.vim/bundle/vundle 
  3. 在.vimrc配置文件中添加vundle支持
    filetype off
    set rtp+=~/.vim/bundle/vundle/
    call vundle#rc() 

    但实际上我是添加一个 ~/.vimrc.bundles文件来保存所有插件的配置,必须在 ~/.vimrc文件加入以下代码片段:

    if filereadable(expand("~/.vimrc.bundles>"))
    source ~/.vimrc.bundles
    endif 

    而 ~/.vimrc.bundles文件内容必须包含:

    filetype off
    set rtp+=~/.vim/bundle/vundle/
    call vundle#rc() 

    你可以复制到我 ~/.vimrc.bundles文件到 Home目录。

安装插件

bundle分为三类,比较常用就是 第二种:

  1. 在Github vim-scripts 用户下的repos,只需要写出repos名称
  2. 在Github其他用户下的repos, 需要写出”用户名/repos名”
  3. 不在Github上的插件,需要写出git全路径
vim: Vim 配置、插件和使用技巧
vim: Vim 配置、插件和使用技巧

Bundle Type.png

将安装的插件在 ~/.vimrc配置,但是我是将插件配置信息放在 ~/.vimrc.bundles:

" Define bundles via Github repos
Bundle 'christoomey/vim-run-interactive'
Bundle 'Valloric/YouCompleteMe'
Bundle 'croaky/vim-colors-github'
Bundle 'danro/rename.vim'
Bundle 'majutsushi/tagbar'
Bundle 'kchmck/vim-coffee-script'
Bundle 'kien/ctrlp.vim'
Bundle 'pbrisbin/vim-mkdir'
Bundle 'scrooloose/syntastic'
Bundle 'slim-template/vim-slim'
Bundle 'thoughtbot/vim-rspec'
Bundle 'tpope/vim-bundler'
Bundle 'tpope/vim-endwise'
Bundle 'tpope/vim-fugitive'
Bundle 'tpope/vim-rails'
Bundle 'tpope/vim-surround'
Bundle 'vim-ruby/vim-ruby'
Bundle 'vim-scripts/ctags.vim'
Bundle 'vim-scripts/matchit.zip'
Bundle 'vim-scripts/tComment'
Bundle >"mattn/emmet-vim"
Bundle >"scrooloose/nerdtree"
Bundle >"Lokaltog/vim-powerline"
Bundle >"godlygeek/tabular"
Bundle >"msanders/snipmate.vim"
Bundle >"jelera/vim-javascript-syntax"
Bundle >"altercation/vim-colors-solarized"
Bundle >"othree/html5.vim"
Bundle >"xsbeats/vim-blade"
Bundle >"Raimondi/delimitMate"
Bundle >"groenewege/vim-less"
Bundle >"evanmiller/nginx-vim-syntax"
Bundle >"Lokaltog/vim-easymotion"
Bundle >"tomasr/molokai"
Bundle >"klen/python-mode&quot; 

打开vim,运行 :BundleInstall或在shell中直接运行 vim +BundleInstall +qall

vim: Vim 配置、插件和使用技巧
vim: Vim 配置、插件和使用技巧

Install Bundle.png

常用插件

NERD Tree

NERD Tree是一个树形目录插件,方便浏览当前目录有哪些目录和文件。

vim: Vim 配置、插件和使用技巧
vim: Vim 配置、插件和使用技巧

NERD Tree Plugin Bundle.png

我在 ~/.vimrc文件中配置NERD Tree,设置一个启用或禁用 NERD Tree的键映射

nmap  :NERDTreeToggle

NERD Tree Configuration.png

所以你只需按 F5键就能启用或禁用 NERD Tree,NERD Tree提供一些常用快捷键来操作目录:

  • 通过 hjkl来移动光标
  • o打开关闭文件或目录,如果想打开文件,必须光标移动到文件名
  • t在标签页中打开
  • s和 i可以水平或纵向分割窗口打开文件
  • p到上层目录
  • P到根目录
  • K到同目录第一个节点
  • P到同目录最后一个节点

YouCompleteMe & syntastic

YouCompleteMe是一个快速、支持模糊匹配的vim代码补全引擎。由于它是基于 Clang引擎为C/C++/Objective-C提供代码提示,也支持其他语言代码提示的引擎,例如基于 Jedi的Python代码补全,基于 OmniSharp的C#代码补全,基于 Gocode的Go代码补全。

vim: Vim 配置、插件和使用技巧
vim: Vim 配置、插件和使用技巧

YouCompleteMe.gif

只需敲入代码,就自动提示想输入的代码列表,你可以选择其中一个,然后 tab键就可以补全代码。

YouCompleteMe已经集成了 Syntastic,所以一旦你编写代码时语法错误,就会有红色错误提示

vim: Vim 配置、插件和使用技巧
vim: Vim 配置、插件和使用技巧
syntastic.png

ctrlp

不知道你有没有遇到这样一种情况:在大规模的工程项目中,目录和文件嵌套比较深,打开一个文件要逐个逐个进入目录才能打开,这样的话,比较耗时间和效率很低, ctrlp重新定义打目录和文件方式,特别适用于大规模项目文件的浏览。

启用ctrlp

  • 运行命令 :CtrlP:CtrlP [starting-directory]来以查找文件模式来启用 ctrlp
  • 运行命令 :CtrlPBuffer:CtrlPMRU来以查找缓冲或最近打开文件模式来启用 ctrlp
  • 运行命令 CtrlPMixed来查找文件、查找缓冲和最近打开文件混合模式来启动 ctrlp

基本使用

  • 在三种查找模式中互相切换
  • 来创建新文件和对应的父目录
  • 来切换到只查找文件名而不是全路径
  • 或箭头方向键来移动查找结果列表
  • 来以新标签或分割窗口的方式来打开文件
  • 来标识或取消标识文件,然后按 来打开文件
  • 来在提示历史中选择下一个/上一个字符串

演示视频

具体如何使用ctrlp,请参考happypetterd的 演示视频,讲解非常清楚。

Vim Powerline

Vim Powerline是一个显示vim状态栏插件,它能够显示vim模式、操作环境、编码格式、行数/列数等信息

vim: Vim 配置、插件和使用技巧
vim: Vim 配置、插件和使用技巧

Vim Powerline.png

Molokai

Molokai是vim颜色主题,效果如下

vim: Vim 配置、插件和使用技巧
vim: Vim 配置、插件和使用技巧

Molokai Color Scheme for Vim.png

常用命令

对于入门vim基本命令可以参考 简明 Vim 练级攻略,以下是本人关于 移动光标、 插入/修改、 删除、 复制、 粘帖、 撤销和恢复等常用命令

  • 移动光标

    1. 对于在 行内移动,通过使用 f/F + 字符来移动到特定的字符,然后再使用 .来重复执行命令; f表示向前移动, F表示向后移动。如果想直接移动到行首或行尾,使用 ^$
    2. 对于在 多行移动,就有多种选择: 第一种是通过 ggG行数 + G指定行数来移动, gg表示移动文件的第一行, G表示移动文件的最后一行, 行数 + G表示移动到特定的行。 第二种就是通过 正则搜索的方式来移动, /string表示正向查找, ?string表示反向查找, n查找下一个匹配的结果, N表示上一个匹配的结果,按 up/down可以浏览搜索历史。 第三种就是使用 标记来移动, m + {a-z}标记位置(适用于单个文件,如果是多个文件,使用大写字母 {A-Z}), `{mark}移动到标记位置的列, ‘{mark}移动到标记位置的行首,还有一些特殊的标记, 表示跳转前光标的位置
  • 选择文本

    v不规则选择

    V按行选择

    Ctrl + V按列选择

  • 插入/修改

    i在当前字符前面插入

    I在行首插入

    a在当前字符后面插入

    A在行尾插入

    o在当前行的下一行插入

    O在当前行的上一行插入

    r更改当前的字符

    R更改多个字符

    cw/caw更改单词

    cf + 字符更改从当前字符到指定字符

    c$更改从当前字符到行尾

    cc更改整行

  • 删除

    x删除字符

    df + 字符删除从当前字符到指定字符

    dw/daw删除单词

    d$删除从当前光标到行尾

    dd删除一行

  • 剪切与粘帖

    dd + pdelete一行,然后放在当前光标下方

    dd + Pdelete一行,然后放在当前光标上方

    dw + pdelete单词,然后放在当前光标后面

    dw + Pdelete单词,然后放在当前光标前面

    p/P可接受计数前缀,重复粘贴

  • 复制

    yw复制单词

    yf复制从当前字符到指定字符

    y$复制当前光标到行尾

    yy复制整行

  • 撤销和恢复

    u撤销

    ctrl + r重做

  • 重复操作

    数字+action表示执行某个操作多少次

    .重复上一个操作

  • 宏录制

    q + 寄存器(a-z)开始录制

    录制动作

    q停止录制

    @ + 寄存器 / @@replay被录制的宏

扩展阅读

原文:http://www.jianshu.com/p/a0b452f8f720

vim: Vim 对中文编码的支持

1、支持中文编码的基础

Vim要更好地支持中文编码需要两个特性:+multi_byte和+iconv,可以用 :version 命令检查当前使用的Vim是否支持,否则的话需要重新编译。

2、影响中文编码的设置项

Vim中有几个选项会影响对多字节编码的支持: encoding(enc):encoding是Vim的内部使用编码,encoding的设置会影响Vim内部的Buffer、消息文字等。在Unix环境下,encoding的默认设置等于locale;Windows环境下会和当前代码页相同。在中文Windows环境下encoding的默认设置是cp936(GBK)。 fileencodings(fenc):Vim在打开文件时会根据fileencodings选项来识别文件编码,fileencodings可以同时设置多个编码,Vim会根据设置的顺序来猜测所打开文件的编码。 fileencoding(fencs) :Vim在保存新建文件时会根据fileencoding的设置编码来保存。如果是打开已有文件,Vim会根据打开文件时所识别的编码来保存,除非在保存时重新设置fileencoding。 termencodings(tenc):在终端环境下使用Vim时,通过termencoding项来告诉Vim终端所使用的编码。

3、Vim中的编码转换

Vim内部使用iconv库进行编码转换,如果这几个选项所设置的编码不一致,Vim就有可能会转换编码。打开已有文件时会从文件编码转换到encoding所设置的编码;保存文件时会从encoding设置的编码转换到fileencoding对应的编码。经常会看到Vim提示[已转换],这是表明Vim内部作了编码转换。终端环境下使用Vim,会从termencoding设置的编码转换到encoding设置的编码。 可以用|:help encoding-values|列出Vim支持的所有编码。

4、具体应用环境的设置

只编辑GBK编码的文件

set fileencodings=cp936
set fileencoding=cp936
set encoding=cp936
 

只编辑UTF-8编码的中文文件

set fileencodings=utf-8
set fileencoding=utf-8
set encoding=cp936 或者 set encoding=utf-8
 

同时支持GBK和UTF-8编码

set fileencodings=ucs-bom,utf-8,cp936
set fileencoding=utf-8
set encoding=cp936 或者 set encoding=utf-8
 

如果在终端环境下使用Vim,需要设置termencoding和终端所使用的编码一致。例如:

set termencoding=cp936 或者 set termencoding=utf-8

Windows记事本编辑UTF-8编码文件时会在文件头上加上三个字节的BOM:EFBBBF。如果fileencodings中设置ucs-bom的目的就是为了能够兼容用记事本编辑的文件,不需要的话可以去掉。Vim在保存UTF-8编码的文件时会去掉BOM。去掉BOM的最大好处是在Unix下能够使用cat a b>c来正确合并文件,这点经常被忽略。

5、FAQ

为什么在Vim中一次只能删除半个汉字?因为encoding设置错误,把encoding设置为cp936就可以解决此问题。在Unix环境下Vim会根据locale来设置默认的encoding,如果没有正确设置locale并且没有设置encoding就会一次只能删除半个汉字。

VIM为什么不能输入繁体字?把euc-cn或者GB2312改为cp936就可以了。euc-cn是GB2312的别名,不支持繁体汉字。cp936是GBK的别名,是GB2312的超集,可以支持繁体汉字。

VIM为什么提示不能转换?因为在编译Vim时没有加入iconv选项,重新编译Vim才能解决。

如何打开一个GBK编码的文件并另存为UTf-8编码? 保存文件时运行命令|:set fileencoding=utf-8|就可以了。

From : CSDN Blog

原文:http://apneng.net/2015/05/09/vim-chinese-language-support.html

vim: VIM查看文件编码、文件编码格式转换及文件名编码转换

如果你需要在Linux中操作windows下的文件,那么你可能会经常遇到文件编码转换的问题。

Windows中默认的文件格式是GBK(gb2312),而Linux一般都是UTF-8。

下面介绍一下,在Linux中如何查看文件的编码及如何进行对文件进行编码转换。

查看文件编码

在Linux中查看文件编码可以通过以下几种方式: 1.在Vim中可以直接查看文件编码 :set fileencoding 即可显示文件编码格式。 如果你只是想查看其它编码格式的文件或者想解决用Vim查看文件乱码的问题,那么你可以在 ~/.vimrc 文件中添加以下内容:

set encoding=utf-8
set fileencodings=ucs-bom,utf-8,cp936,gb18030,big5,euc-jp,euc-kr,latin1
 

这样,就可以让vim自动识别文件编码(可以自动识别UTF-8或者GBK编码的文件),其实就是依照fileencodings提供的编码列表尝试,如果没有找到合适的编码,就用latin-1(ASCII)编码打开。

文件编码转换

  1. 在Vim中直接进行转换文件编码,比如将一个文件转换成utf-8格式

    :set fileencoding=utf-8

  2. iconv 转换,iconv的命令格式如下:

    iconv -f encoding -t encoding inputfile 比如将一个UTF-8 编码的文件转换成GBK编码

    iconv -f GBK -t UTF-8 file1 -o file2

文件名编码转换:

从Linux往 windows拷贝文件或者从windows往Linux拷贝文件,有时会出现中文文件名乱码的情况,出现这种问题的原因是因为,windows的文件名中文编码默认为GBK,而Linux中默认文件名编码为UTF8,由于编码不一致,所以导致了文件名乱码的问题,解决这个问题需要对文件名进行转码。

在Linux中专门提供了一种工具convmv进行文件名编码的转换,可以将文件名从GBK转换成UTF-8编码,或者从UTF-8转换到GBK。

首先看一下你的系统上是否安装了convmv,如果没安装的话用:

yum -y install convmv

安装。

下面看一下convmv的具体用法:

convmv -f 源编码 -t 新编码 [选项] 文件名

常用参数:

-r 递归处理子文件夹
--notest 真正进行操作,请注意在默认情况下是不对文件进行真实操作的,而只是试验。
--list 显示所有支持的编码
--unescap 可以做一下转义,比如把%20变成空格
 

比如我们有一个utf8编码的文件名,转换成GBK编码,命令如下:

convmv -f UTF-8 -t GBK --notest utf8编码的文件名

这样转换以后”utf8编码的文件名”会被转换成GBK编码(只是文件名编码的转换,文件内容不会发生变化)

Vim 编码方式的设置

和所有的流行文本编辑器一样,Vim 可以很好的编辑各种字符编码的文件,这当然包括UCS-2、UTF-8 等流行的 Unicode 编码方式。然而不幸的是,和很多来自 Linux 世界的软件一样,这需要你自己动手设置。

Vim 有四个跟字符编码方式有关的选项,encoding、fileencoding、fileencodings、termencoding (这些选项可能的取值请参考 Vim 在线帮助 :help encoding-names),它们的意义如下:

  • encoding: Vim 内部使用的字符编码方式,包括 Vim 的 buffer (缓冲区)、菜单文本、消息文本等。默认是根据你的locale选择.用户手册上建议只在 .vimrc 中改变它的值,事实上似乎也只有在.vimrc 中改变它的值才有意义。你可以用另外一种编码来编辑和保存文件,如你的vim的encoding为utf-8,所编辑的文件采用cp936编码,vim会自动将读入的文件转成utf-8(vim的能读懂的方式),而当你写入文件时,又会自动转回成cp936(文件的保存编码).

  • fileencoding: Vim 中当前编辑的文件的字符编码方式,Vim 保存文件时也会将文件保存为这种字符编码方式 (不管是否新文件都如此)。

  • fileencodings: Vim自动探测fileencoding的顺序列表, 启动时会按照它所列出的字符编码方式逐一探测即将打开的文件的字符编码方式,并且将 fileencoding 设置为最终探测到的字符编码方式。因此最好将Unicode 编码方式放到这个列表的最前面,将拉丁语系编码方式 latin1 放到最后面。

  • termencoding: Vim 所工作的终端 (或者 Windows 的 Console 窗口) 的字符编码方式。如果vim所在的term与vim编码相同,则无需设置。如其不然,你可以用vim的termencoding选项将自动转换成term的编码.这个选项在 Windows 下对我们常用的 GUI 模式的 gVim 无效,而对 Console 模式的Vim 而言就是 Windows 控制台的代码页,并且通常我们不需要改变它。

好了,解释完了这一堆容易让新手犯糊涂的参数,我们来看看 Vim 的多字符编码方式支持是如何工作的。

  1. Vim 启动,根据 .vimrc 中设置的 encoding 的值来设置 buffer、菜单文本、消息文的字符编码方式。

  2. 读取需要编辑的文件,根据 fileencodings 中列出的字符编码方式逐一探测该文件编码方式。并设置 fileencoding 为探测到的,看起来是正确的 (注1) 字符编码方式。

  3. 对比 fileencoding 和 encoding 的值,若不同则调用 iconv 将文件内容转换为encoding 所描述的字符编码方式,并且把转换后的内容放到为此文件开辟的 buffer 里,此时我们就可以开始编辑这个文件了。注意,完成这一步动作需要调用外部的 iconv.dll(注2),你需要保证这个文件存在于 $VIMRUNTIME 或者其他列在 PATH 环境变量中的目录里。

  4. 编辑完成后保存文件时,再次对比 fileencoding 和 encoding 的值。若不同,再次调用 iconv 将即将保存的 buffer 中的文本转换为 fileencoding 所描述的字符编码方式,并保存到指定的文件中。同样,这需要调用 iconv.dll由于 Unicode 能够包含几乎所有的语言的字符,而且 Unicode 的 UTF-8 编码方式又是非常具有性价比的编码方式 (空间消耗比 UCS-2 小),因此建议 encoding 的值设置为utf-8。这么做的另一个理由是 encoding 设置为 utf-8 时,Vim 自动探测文件的编码方式会更准确 (或许这个理由才是主要的 ;)。我们在中文 Windows 里编辑的文件,为了兼顾与其他软件的兼容性,文件编码还是设置为 GB2312/GBK 比较合适,因此 fileencoding 建议设置为 chinese (chinese 是个别名,在 Unix 里表示 gb2312,在 Windows 里表示cp936,也就是 GBK 的代码页)。

原文:http://apneng.net/2015/05/09/vim-file-encoding.html

vim: Vim常用命令手册

这两年工作基本都是用vim,用习惯发现到哪都离不开这玩意。
  • 退出编辑器
:w     将缓冲区写入文件,即保存修改
:wq     保存修改并退出
:x     保存修改并退出
:q     退出,如果对缓冲区进行过修改,则会提示
:q!     强制退出,放弃修改
--->
我个人比较喜欢的是:大写ZZ退出 
  • 查找替换
/pattern     向后搜索字符串pattern
?pattern     向前搜索字符串pattern
n     下一个匹配(如果是/搜索,则是向下的下一个,?搜索则是向上的下一个)
N     上一个匹配(同上)
:%s/old/new/g     搜索整个文件,将所有的old替换为new
:%s/old/new/gc     搜索整个文件,将所有的old替换为new,每次都要你确认是否替换
--->
同时替换配合正则绝对是一个强大的删除命令: 
删除以#开头的:    s/^#.*$//g 
如果有些先以空格开头,并且顺便想删除换行驶符:  s/^[ ]*#.*n//g
删除空白行:  g/^$/d 
  • 复制粘贴
dd     删除光标所在行
dw     删除一个字(word)
x     删除当前字符
X     删除前一个字符
D     删除到行末
yy     复制一行,此命令前可跟数字,标识复制多行,如6yy,表示从当前行开始复制6行
yw     复制一个字
y$     复制到行末
p     粘贴粘贴板的内容到当前行的下面
P     粘贴粘贴板的内容到当前行的上面
]p     有缩进的粘贴,vim会自动调节代码的缩进
&quot;a     将内容放入/存入a寄存器,可以支持多粘贴板
--->
复制粘贴配合可视化操作配合移动光标,绝壁是最人性化最舒服的复制好吗?
按v进入可视化,选定后按y,定位光标再p 
  • 移动光标
在vim中移动光标跟其他的编辑器中有很大的区别,不过一旦学会了,就会飞速的在文本中移动了。
h,j,k,l     上,下,左,右
ctrl-f     上翻一页
ctrl-b     下翻一页
%     跳到与当前括号匹配的括号处,如当前在{,则跳转到与之匹配的}处
w     跳到下一个字首,按标点或单词分割
W     跳到下一个字首,长跳,如end-of-line被认为是一个字
e     跳到下一个字尾
E     跳到下一个字尾,长跳
b     跳到上一个字
B     跳到上一个字,长跳
0     跳至行首,不管有无缩进,就是跳到第0个字符
^     跳至行首的第一个字符
$     跳至行尾
gg     跳至文件的第一行
gd     跳至当前光标所在的变量的声明处
[N]G     跳到第N行,如0G,就等价于gg,100G就是第100行
fx     在当前行中找x字符,找到了就跳转至
;     重复上一个f命令,而不用重复的输入fx
tx     与fx类似,但是只是跳转到x的前一个字符处
Fx     跟fx的方向相反
),(     跳转到上/下一个语句
*     查找光标所在处的单词,向下查找
#     查找光标所在处的单词,向上查找
`.     跳转至上次编辑位置
在屏幕上移动
H     移动光标到当前屏幕上最上边的一行
M     移动光标到当前屏幕上中间的一行
L     移动光标到当前屏幕上最下边的一行
--->
绝壁是vim的入门课 
  • 书签
ma     把当前位置存成标签a
`a     跳转到标签a处
  • 编辑
r     替换一个字符
J     将下一行和当前行连接为一行
cc     删除当前行并进入编辑模式
cw     删除当前字,并进入编辑模式
c$     擦除从当前位置至行末的内容,并进入编辑模式
s     删除当前字符并进入编辑模式
S     删除光标所在行并进入编辑模式
xp     交换当前字符和下一个字符
u     撤销
ctrl+r     重做
.     重复上一个编辑命令
~     切换大小写,当前字符
g~iw     切换当前字的大小写
gUiw     将当前字变成大写
guiw     将当前字变成小写
>>     将当前行右移一个单位
<<     将当前行左移一个单位(一个tab符)
==     自动缩进当前行
--->
在vim下,ctrl + v 可以进入列模式,
在gvim下, ctrl + q可进入列模式。
列模式对于我来说,干的最多的一件事就是注释大段的代码了。
ctrl + v,
放好列的位置,
按大写I,
输入#,
esc 
  • 插入模式
i     从当前光标处进入插入模式
I     进入插入模式,并置光标于行首
a     追加模式,置光标于当前光标之后
A     追加模式,置光标于行末
o     在当前行之下新加一行,并进入插入模式
O     在当前行之上新加一行,并进入插入模式
Esc     退出插入模式 
  • 可视模式
标记文本
v     进入可视模式,单字符模式
V     进入可视模式,行模式
ctrl+v     进入可视模式,列模式,类似于UE的列模式
o     跳转光标到选中块的另一个端点
U     将选中块中的内容转成大写
O     跳转光标到块的另一个端点
aw     选中一个字
ab     选中括号中的所有内容,包括括号本身
aB     选中{}括号中的所有内容
ib     选中括号中的内容,不含括号
iB     选中{}中的内容,不含{}
对标记进行动作
>     块右移
<     块左移
y     复制块
d     删除块
~     切换块中内容的大小写
 &#91;/code&#93;
<ul>
<li>
标签命令</li>
</ul>

:tabe fn     在一个新的标签页中编辑文件fn
gt     切换到下一个标签页
gT     切换到上一个标签页
:tabr     切换到第一个标签页
:tabl     切换到最后一个标签页
:tabm [N]     把当前tab移动到第N个tab之后 
  • 窗口命令
ctrl+w s     水平分割窗口
ctrl+w w     切换窗口
ctrl+w q     退出当前窗口(由于同时有多个文件,此命令不会影响其他窗口)
ctrl+w v     垂直分割窗口 
  • 其他
vim在保存之前不会对文件做实际的修改,只是加载到缓冲区中,对文件的编辑其实是对缓冲区的编辑,直到:w时才会存入物理文件。
:e file     把file加载到新的缓冲区中
:bn     跳转到下一个缓冲区
:bd     删除缓冲区(关闭文件)
:sp fn     分割窗口,并将fn加载到新的窗口中 

原文:http://www.cnblogs.com/clor001/p/3876795.html

vim: Vim 自动补全插件 YouCompleteMe 安装与配置

概述

对于代码自动补全,之前一直使用的是Shougo/neocomplcache和Shougo/neosnippet。早就听说过YouCompleteMe的大名,一直想尝试一下YCM,但是还是拖到了现在。YCM和其它Vim插件的安装有些不同,可能需要折腾一下。之所以安装稍微会麻烦些,是因为YCM 后端调用 libclang(以获取AST,当然还有其他语言的语义分析库)、前端由 C++ 开发(以提升补全效率)、外层由 python 封装(以成为 vim 插件),它可能是安装最复杂的 vim 插件了。YCM是Client-sever架构的,Vim这部分的YCM只是很小的一个客户端,与具有大量逻辑和功能的ycmd HTTP+JSON交互。server在你开启或关闭Vim是自动开启或关闭。

其它自动补全的插件基本上是基于文本的,也就是说它们基本上是使用正则去猜。区别于其它Vim的自动补全插件,YCM基于语义引擎(比如C家族的libclang)提供了语义补全,是通过分析源文件,经过语法分析之后进行补全。对于C家族的语言这种基于语义的自动补全依赖于clang/llvm,其他语言 ,会调用vim设置的omnifunc来匹配,可以查看 github

YCM不是基于前缀补全的,而是子序列,所以输入 abc 可以补全 xaybgc,它对于大小写的补全也非常智能。对于C家族的语言和Python支持跳转到定义处。此外还可以对文件路径进行补全,和ultisnips也很好结合。

安装

完全安装

如果用Vundle更新YCM,yum_support_lib库API改变了,YCM会提醒你重新编译它。

  1. 确保Vim版本至少是7.3.584,并且支持python2脚本。

    在Vim中输入 :version可以查看版本。如果版本已经高于7.4了,那么OK。版本是7.3。那么继续往下看,看到 包含版本:1-Z,如果Z小于584就需要重装了。Ubuntu的话可以通过 PPA安装高版本的。否则就要从源码编译安装了。

    查看是否支持python2:进入vim,命令:echo has(‘python’),输出为1,则表示支持。如果为0,则需要重新编译安装vim,在编译时添加python支持。

  2. 通过Vundle安装YCM,在你的vimrc中添加 Plugin ‘Valloric/YouCompleteMe’,然后执行 :PluginInstall

  3. 如果不需要对C家族的语言进行语义补全支持,则跳过这一步。

    下载最新版的libclang。Clang是一个开源编译器,能够编译C/C++/Objective-C/Objective-C++。Clang提供的libclang库是用于驱动YCM对这些语言的语义补全支持。YCM需要版本至少为3.6的libclang,但是理论上3.2+版本也行。也可以使用系统libclang,如果确定是3.3版本或者更高。推荐下载
    官网的 二进制文件。确保选对适合自己系统的包。

  4. 编译YCM需要的ycm_support_libs库。YCM的C++引擎通过这些库来获取更快的补全速度。

    需要cmake,如果未安装,安装之: sudo apt-get install build-essential cmake(也可以下载安装 http://www.cmake.org/cmake/resources/software.html)。确保python头文件已安装: sudo apt-get install python-dev。假设你已经通过Vundle装好YCM了,那么它应该位于 ~/.vim/bundle/YouCompleteMe。我们新建一个目录用来放置编译文件,并切换到此目录下 cd ~;mkdir ycm_build;cd ycm_build;

    下一步生成makefile,这一步很重要,有点复杂。

    • 如果不需要C族语言的语义支持,在ycm_build目录下执行: cmake -G “Unix Makefiles>” . ~/.vim/bundle/YouCompleteMe/third_party/ycmd/cpp
    • 如果需要C族语言的语义支持,还得分几种情况:

      • 假如你从llvm的官网下载了LLVM+Clang,然后解压到:~/ycm_temp/llvm_root_dir (该目录下有 bin, lib, include 等文件夹),然后执行: cmake -G “Unix Makefiles>” -DPATH_TO_LLVM_ROOT=~/ycm_temp/llvm_root_dir . ~/.vim/bundle/YouCompleteMe/third_party/ycmd/cpp
      • 如果想用系统的libclang: cmake -G “Unix Makefiles>” -DUSE_SYSTEM_LIBCLANG=ON . ~/.vim/bundle/YouCompleteMe/third_party/ycmd/cpp
      • 如果想用自定义的libclang: cmake -G “Unix Makefiles>” -DEXTERNAL_LIBCLANG_PATH=/path/to/libclang.so . ~/.vim/bundle/YouCompleteMe/third_party/ycmd/cpp。/path/to/libclang.so这部分填入你自己的路径。
        >
        makefiles生成后执行: make ycm_support_libs

我是从llvm网站下载的二进制文件,安装的,安装过程中也没遇到什么问题。

Ubuntu Linux X64 超快安装

最好还是完整安装,这种快速安装未必适合所有人。安装之前,同样也要确定满足以上所述的vim版本、python支持等条件。和上面一样使用Vundle安装YCM,安装CMake和python头文件。

编译YCM,如果需要对C-family的语义支持:

cd
~/. vim
/bundle/YouCompleteMe

./install. sh
--clang-completer

如果不需要对C-family的语义支持:

cd
~/. vim
/bundle/YouCompleteMe

./install. sh

如果需要支持C#,添加 --omnisharp-complete。如果需要支持Go添加 --gocode-completer

配置

YCM使用TAB键接受补全,一直按TAB则会循环所有的匹配补全项。shift+TAB则会反向循环。注意:如果使用控制台Vim(非GVim或MacVim等),控制台不会将shift+Tab传递给Vim,因此会无反应,需要重新映射按键。此外,如果同时使用ultisnaps,可能会有冲突,需要进行一些设置。可以使用Ctrl+Space来触发补全,不过会和输入法冲突,也需要设置。

YCM会寻找当前打开的文件的同级目录下或上级目录中的 ycm_extra_conf.py这个文件,找到后会加载为Python模块,且只加载一次。YCM调用该模块中的FlagsForFile方法。该模块必须提供带有编译当前文件的必要信息的这个方法。需要修改 .ycm_extra_conf.py文件中的flags部分,使用-isystem添加系统的头文件进行解析,使用-I添加第三方的头文件进行解析,在flags部分后添加如下内容:

'-isystem'
,

'/usr/include'
,

使用命令 :YcmDiags可以打开location-list查看警告和错误信息。

set completeopt=longest,menu "让Vim的补全菜单行为与一般IDE一致(参考VimTip1228)

autocmd InsertLeave * if pumvisible() == 0|pclose|endif >"离开插入模式后自动关闭预览窗口

inoremap pumvisible() ? ">"
: ">"
"回车即选中当前项

>"上下左右键的行为 会显示其他信息

inoremap pumvisible() ? ">"
: ">"

inoremap pumvisible() ? ">"
: ">"

inoremap pumvisible() ? ">"
: ">"

inoremap pumvisible() ? ">"
: ">"

" 跳转到定义处

nnoremap jd :YcmCompleter GoToDefinitionElseDeclaration

nnoremap :YcmForceCompileAndDiagnostics >"force recomile with
syntastic

" nnoremap lo :lopen >"
open locationlist

" nnoremap lc :lclose >"
close locationlist

inoremap

let
g
:ycm_global_ycm_extra_conf = '~/.vim/data/ycm/.ycm_extra_conf.py'

" 不显示开启vim时检查ycm_extra_conf文件的信息

let g:ycm_confirm_extra_conf=0

>" 开启基于tag的补全,可以在这之后添加需要的标签路径

let
g
:ycm_collect_identifiers_from_tags_files= 1

"注释和字符串中的文字也会被收入补全

let g:ycm_collect_identifiers_from_comments_and_strings = 0

>" 输入第 2
个字符开始补全

let
g
:ycm_min_num_of_chars_for_completion= 2

" 禁止缓存匹配项,每次都重新生成匹配项

let g:ycm_cache_omnifunc=0

>" 开启语义补全

let
g
:ycm_seed_identifiers_with_syntax= 1

"在注释输入中也能补全

let g:ycm_complete_in_comments = 1

>"在字符串输入中也能补全

let
g
:ycm_complete_in_strings = 1

" 设置在下面几种格式的文件上屏蔽ycm

let g:ycm_filetype_blacklist = {

'tagbar' : 1,

'nerdtree' : 1,

}

>"youcompleteme 默认tab s-tab 和 ultisnips 冲突

let
g
:ycm_key_list_select_completion = [ ''
]

let
g
:ycm_key_list_previous_completion = [ ''
]

" 修改对C函数的补全快捷键,默认是CTRL + space,修改为ALT + ;

let g:ycm_key_invoke_completion = ''

>" SirVer/ultisnips 代码片断

" Trigger configuration. Do not use if you use https://github.com/Valloric/YouCompleteMe.

let g:UltiSnipsExpandTrigger=>" "

let g:UltiSnipsJumpForwardTrigger=>" "

let g:UltiSnipsJumpBackwardTrigger=>" "

let g:UltiSnipsListSnippets=>" "

>"定义存放代码片段的文件夹,使用自定义和默认的,将会的到全局,有冲突的会提示

let
g
:UltiSnipsSnippetDirectories=[ "bundle/vim-snippets/UltiSnips>"
]

" 参考https://github.com/Valloric/YouCompleteMe/issues/36#issuecomment-62941322

>" 解决ultisnips和ycm tab冲突,如果不使用下面的办法解决可以参考

" https://github.com/Valloric/YouCompleteMe/issues/36#issuecomment-63205056的配置

>" begin

" let g:ycm_key_list_select_completion=['', '']

>" let
g
:ycm_key_list_previous_completion=[ ''
, ''
]

" let g:UltiSnipsExpandTrigger=>"
"

>" let
g
:UltiSnipsJumpForwardTrigger= ">"

" let g:UltiSnipsJumpBackwardTrigger=>"
"

>" end

" UltiSnips completion function that tries to expand a snippet. If there's no

>" snippet for
expanding, it checks for
completion window
and
if
it 's

" shown, selects first element. If there's no
completion window
it tries to

>" jump to next placeholder. If there's no placeholder it just returns TAB key

function! g:UltiSnips_Complete()

call UltiSnips#ExpandSnippet()

if g:ulti_expand_res == 0

if pumvisible()

return " >"

else

call UltiSnips#JumpForwards()

if g:ulti_jump_forwards_res == 0

return " >"

endif

endif

endif

return " >"

endfunction

au BufEnter * exec "inoremap >" . g:UltiSnipsExpandTrigger . "
= g
:UltiSnips_Complete() >"

" Expand snippet or
return

let
g
:ulti_expand_res = 1

function
! Ulti_ExpandOrEnter()

call UltiSnips #ExpandSnippet()

if
g
:ulti_expand_res

return
''

else

return
>""

endfunction

>" Set as primary trigger

inoremap =Ulti_ExpandOrEnter()

YCM & Eclim

YCM配合Eclim可以实现对Java代码的自动提示,首先需要下载 Eclim,下载好后执行 java -jar eclim_2.4.1.jar,卸载的话,后面再加个参数 uninstaller就行。按提示一步步安装就好了,安装好后首先要启动eclimd,它存放在eclipse的根目录下。

对不同的自动补全插件eclim都提供了 配置方法,对于YCM我们只需要在vimrc中添加一行 let g:EclimCompletionMethod = 'omnifunc'就可以了。

打开vim执行 :ProjectCreate /path/to/project -n java创建一个新的工程。这个工程的结构和eclipse类似。 :ProjectList命令可以查看工程列表。

这个插件很有意思,可以在eclipse中嵌入vim,又可以在vim中享受eclipse一样的自动补全。对于Java来说,提供了一些以 JavaProjectNewMvn为前缀的命名。可以输入 :Java按Tab键尝试一下。不过,我觉得还是使用eclipse配合viPlugin插件更方便些。

参考:

原文:http://howiefh.github.io/2015/05/22/vim-install-youcompleteme-plugin/

vim: 静静地,做个vim装13党

虽然敲了好几年代码才接触到vim;

虽然有了sublime text和vsCode;

虽然还没接触到mac里的神(niu)马(bi)编辑器;

但是,这一刻

请让我

静静地,做个vim装13党

于是,我在vs2012里安装了vsvim插件。

遇见

在某个秋天,知道了cs50,看了几节David的课程,发现他貌似敲命令和写代码都是在一个黑乎乎的界面完成,然后网页就刷刷刷的变了,真是好奇!于是,baidu+google的一顿翻,原来是个叫 vim的东西在作怪。

发展

遇见了vim,作为一只爱鼓捣的金刚猪(程序员),当然要各种办法试用一下。于是,从window到ubuntu,从cygwin到gvim到内置的vim,从 vi tutor的毛手毛脚到逐渐记住了 iwq,勉强在vim的世界活了下来。 但是一直还是偶尔隔几个月想起来敲一下而已,学习进度缓慢。直到遇见了 实验楼,又稍微认真的学习了两天后,终于多记住了几个按键的作用。特别是发现了下面这张图并把它作为桌面后,事情就越来越好玩了。

vim: 静静地,做个vim装13党
vim: 静静地,做个vim装13党

选择

学习vim时候,在搜索过程中不可避免的遇见vim的老相好,另一款最好的编辑器 Emacs,他们都是最好的,嘿嘿我不较真,详细 点这里

但是吧,人总是会作出最适合自己的选择,虽然我的同步盘里一直也放着Emacs,对各种组合键也是很喜欢,但是在与工作相结合的时候,也就是考虑怎么在vs里玩这种靠键盘写代码的工具时候,vim无疑是我最好的选择,单独的按键功能,配合vs自带的各种组合键。我就可以开心的在代码里遨游了。

继续

虽然用vim前前后后加起来快3年,但是很多功能还是几乎没用过的,我还是会继续学习的,恰好的是,最近网上有个很不错的帖子,把各种高级的用法图都贴了出来。于是,就保存起来好好学习了。主要学习这个图的。

vim: 静静地,做个vim装13党
vim: 静静地,做个vim装13党

具体帖子地址是 史上最全Vim快捷键键位图 — 入门到进阶.

说到底,在vim的世界里,我还只是个孩子。

请大牛们多多指教!

原文:http://www.ituring.com.cn/article/198444

vim: Mac 下配置 Vim 代码补全:YouCompleteMe

引言

Vim 无疑是世界上最好用的编辑器之一(为了不引起战争 →_→)。在广大程序员用 Vim 敲代码的过程中,代码补全功能能够大大提高生产力,尤其是对于从各种 IDE 转到 Vim 的程序员来说更是福音般地存在。本文将介绍一种推荐的代码补全工具 YouCompleteMe,并且一步步介绍它的安装方式。

YouCompleteMe

YouCompleteMe
是一个比较完备,并且正在日渐完备的 Vim 代码补全插件。它的功能十分强大,支持自动补全的语言包括:

  • C/C++/Objective-C/Objective-C++ (基于 Clang
    )
  • Python (基于 Jedi
    )
  • C# (基于 OmniSharp
    )
  • Go (基于 GoCode
    )
  • 其他 Vim 的 omnicomplete system 支持的语言,比如 (Ruby, PHP 等)

先上一张作者给的 demo 效果图。

vim: Mac 下配置 Vim 代码补全:YouCompleteMe
vim: Mac 下配置 Vim 代码补全:YouCompleteMe

安装

接下来介绍安装过程。

确定 Vim 版本满足支持

如果你用的是 Mac OS X 自带的 vim 的话,那么肯定是不能满足需求的。首先,需要升级 vim。这里建议安装 macvim
,当然 vim 的 官网
也是这么推荐的。推荐使用 brew
来安装。

  1. MacVim 依赖 Xcode,首先需要在 App Store 中安装 Xcode。如果是全新安装的 Xcode,请打开一次,并且同意 license。
  2. 使用 brew 安装 MacVim

    bashbrew install macvim
    
  3. 使用 MacVim 替换系统自带的 Vim,在当前 shell 的配置文件中添加

    bashalias vim='mvim -v'
    

安装 Vundle

Vundle (缩写自 Vim bundle) 是一个很方便的 Vim 插件管理器。它的使用方法很简单,安装一个插件只需要在 .vimrc按照规则中添加 Plugin 的名称,某些需要添加路径,之后在 Vim 中使用 :PluginInstall既可以自动化安装。具体的使用过方法详见 官网

下面将介绍 Vundle 的安装及基本配置。

  1. 使用 git 克隆 Vundle 工程到本地。

    bash
    git clone https://github.com/gmarik/Vundle.vim.git ~/.vim/bundle/Vundle.vim
     
  2. 修改
    .vimrc
    配置 Plugins。在
    .vimrc
    文件中添加如下内容。

    set nocompatible
    filetype off
    set rtp+=~/.vim/bundle/Vundle.vim
    call vundle#begin()
    Plugin 'gmarik/Vundle.vim'
    call vundle#end()
    filetype plugin indent on
     

安装 YouCompleteMe

接下来将要安装的是我们的主角,YouCompleteMe。解决了上面的依赖软件之后,安装它将变得非常简单。


  1. .vimrc
    中添加如下内容。位置在
    call vundle#begin()

    call vundle#end()
    之间。

    Bundle 'Valloric/YouCompleteMe'
    
  2. 编译 YouCompleteMe

    首先说明,编译过程需要
    CMake
    ,可以使用
    brew
    来安装。

    bash
    brew install CMake
    
    • 带 C-family languages 语义支持的版本
    bash
    cd ~/.vim/bundle/YouCompleteMe
    ./install.sh --clang-completer
     
    • 不带 C-family languages 语义支持的版本
    bash
    cd ~/.vim/bundle/YouCompleteMe
    ./install.sh --clang-completer
     
    • 带 C# 语义支持的版本
    bash
    cd ~/.vim/bundle/YouCompleteMe
    ./install.sh --omnisharp-completer
     
    • 带 Go 语言语义支持的版本
    bash
    cd ~/.vim/bundle/YouCompleteMe
    ./install.sh --gocode-completer
     

完成

至此,我们已经拥有了 YouCompleteMe 这款自动补全神器。请尽情享用。

Just enjoy it !

原文:http://segmentfault.com/a/1190000002793897

vim: 玩转VIM-札记(三)

玩转VIM-札记(三)

眨眼之间,5月就要从指间溜走,不给人一点点遐想的时间,我要赶紧抓着五月的尾巴,在博客中在添一笔。那么就还接着Vim来说吧。以Vim来为五月画上一个句号。

返璞归真

相信经过玩转Vim-札记(一)和玩转Vim-札记(二)的学习,对于光标的移动已经能做到随心所欲了,但是如果想要更快速的操作,以下几个光标移动的命令也是必不可少的。

在当前行上移动光标:  0^$fFtT,;

  • 0 → 到行头
  • ^ → 到本行的第一个非blank字符
  • $ → 到行尾
  • g_ → 到本行最后一个不是blank字符的位置。
  • fa → 到下一个为a的字符处,你也可以fs到下一个为s的字符。
  • t, → 到逗号前的第一个字符。逗号可以变成其它字符。
  • 3fa → 在当前行查找第三个出现的a。
  • F 和  T → 和  f 和  t 一样,只不过是相反方向。

区域选择  a或  i

在visual 模式下,这些命令很强大,其命令格式为

a和  i

  • action可以是任何的命令,如  d (删除),  y (拷贝),  v (可以视模式选择)。
  • object 可能是:  w 一个单词,  W 一个以空格为分隔的单词,  s 一个句字,  p 一个段落。也可以是一个特别的字符: "、  ‘、  )、  }、  ]。

假设你有一个字符串  (map (+) (“foo>”)).而光标键在第一个  的位置。

  • vi” → 会选择  foo.
  • va>” → 会选择  “foo>”.
  • vi) → 会选择  “foo>”.
  • va) → 会选择 (“foo>”).
  • v2i) → 会选择  map (+) (“foo>”)
  • v2a) → 会选择  (map (+) (“foo>”))

块操作: 

块操作,典型的操作:  0 I– [ESC]

  • ^ → 到行头
  •  → 开始块操作
  •  → 向下移动 (你也可以使用hjkl来移动光标,或是使用%,或是别的)
  • I– [ESC] → I是插入,插入“ ”,按ESC键来为每一行生效。

在Windows下的vim,你需要使用  而不是  是拷贝剪贴板。

自动提示:   和 

在 Insert 模式下,你可以输入一个词的开头,然后按  或是,自动补齐功能就出现了……

宏录制:  qa 操作序列  q@a@@

  • qa 把你的操作记录在寄存器  a。
  • 于是  @a 会replay被录制的宏。
  • @@ 是一个快捷键用来replay最新录制的宏。
  • 示例

    在一个只有一行且这一行只有“1”的文本中,键入如下命令:

    • qaYpq@a → 在1下面写下 2
      • qa 开始录制
      • Yp 复制行.
      •  增加1.
      • q 停止录制.
    • @@ → 在2 正面写下3
    • 现在做  100@@ 会创建新的100行,并把数据增加到 103.

可视化选择:  v, V,

前面,我们看到了  的示例 (在Windows下应该是),我们可以使用  v和  V。一但被选好了,你可以做下面的事:

  • J → 把所有的行连接起来(变成一行)
  • < 或  > → 左右缩进
  • = → 自动给缩进 

在所有被选择的行后加上点东西:

  • 选中相关的行 (可使用  j 或   或是  /pattern 或是  % 等……)
  • $ 到行最后
  • A, 输入字符串,按  ESC。

分屏:  :split 和  vsplit

下面是主要的命令,你可以使用VIM的帮助  :help split. 你可以参考陈皓以前的一篇文章 VIM分屏

  • :split → 创建分屏 ( :vsplit创建垂直分屏)
  •  : dir就是方向,可以是  hjkl 或是 ←↓↑→ 中的一个,其用来切换分屏。
  • _ (或  |) : 最大化尺寸 (| 垂直分屏)
  • + (或  ) : 增加尺寸

参考:http://yannesposito.com/Scratch/en/blog/Learn-Vim-Progressively/

PS:本博客欢迎转发,但请注明博客地址及作者,因本人水平有限,若有不对之处,欢迎指出,谢谢~

博客地址: http://www.cnblogs.com/voidy/

博客新址: http://voidy.net

<。)#)))≦

原文:http://www.cnblogs.com/voidy/p/4541530.html

vim: Vim配置、插件和使用技巧

vim_cheat_sheet_for_programmers.png

常言道:工欲善其事,必先利其器 ,作为一个程序员,一个常用的工具就是 编辑器,我选择一个能极大提高自己开发效率的编辑器 vim(有些人可能选择 emacs)。而 vim编辑器方面具有以下几种特性:

  • 跨平台及统一环境无论是在windows还是在*nix,vim是一个很完美的跨平台文本编辑器,甚至可以直接在服务器平台CentOS,Ubuntu等直接配置使用,配置文件大同小异,操作习惯基本相同。

  • 定制化及可扩展

    vim提供一个 vimrc的配置文件来配置vim,并且自己可以定制一些插件来实现文件浏览( NERD Tree),代码补全( YouCompleteMe,语法检查( syntastic),文件模糊搜索( ctrlp),显示vim状态栏( Vim Powerline),主题颜色( Molokai),显示文件结构( tagbar)等多种功能。

  • 高效命令行使用vim编辑文本,只需在键盘上操作就可以,根本无需用到鼠标。就拿光标移动来说,与重复击键、一个字符一个字符或一行一行移动相比,按一次键就能以词、行、块或函数为单位移动,效率高得多。有时一些重复删除、粘帖的操作,也只需一条命令就可以完成,甚至你可以用键映射来简化或组合多种命令来提高效率。

配置

如果你需要配置vim,只需在Home目录创建一个 ~/.vimrc文件即可以配置vim了,可以参考我的 vimrc配置文件。由于我需要安装插件,并且将需要安装的插件列表分离到另外一个文件 ~/.vimrc.bundles,这个文件也是存放在Home目录,文件内容可以参考 vimrc.bundles。若想加载 ~/.vimrc.bundles文件,必须在 ~/.vimrc文件加入以下代码片段:

if filereadable(expand("~/.vimrc.bundles>"))   source ~/.vimrc.bundles endif 

插件

插件管理工具vunble

vundle是vim的插件管理工具,它能够搜索、安装、更新和移除vim插件,再也不需要手动管理vim插件。

  1. 在 Home目录创建 ~/.vim目录和 .vimrc文件(可复制我的 vimrc文件)
  2. 安装vundle

    git clone https://github.com/gmarik/vundle.git ~/.vim/bundle/vundle 
  3. 在.vimrc配置文件中添加vundle支持

    filetype off set rtp+=~/.vim/bundle/vundle/ call vundle#rc() 

    但实际上我是添加一个 ~/.vimrc.bundles文件来保存所有插件的配置,必须在 ~/.vimrc文件加入以下代码片段:

    if filereadable(expand("~/.vimrc.bundles>")) source ~/.vimrc.bundles endif 

    而 ~/.vimrc.bundles文件内容必须包含:

    filetype off set rtp+=~/.vim/bundle/vundle/ call vundle#rc() 

    你可以复制到我
    ~/.vimrc.bundles
    文件到 Home目录。

安装插件

bundle分为三类,比较常用就是 第二种:

  1. 在Github vim-scripts 用户下的repos,只需要写出repos名称
  2. 在Github其他用户下的repos, 需要写出”用户名/repos名”
  3. 不在Github上的插件,需要写出git全路径

Bundle Type.png

将安装的插件在 ~/.vimrc配置,但是我是将插件配置信息放在 ~/.vimrc.bundles:

" Define bundles via Github repos Bundle 'christoomey/vim-run-interactive' Bundle 'Valloric/YouCompleteMe' Bundle 'croaky/vim-colors-github' Bundle 'danro/rename.vim' Bundle 'majutsushi/tagbar' Bundle 'kchmck/vim-coffee-script' Bundle 'kien/ctrlp.vim' Bundle 'pbrisbin/vim-mkdir' Bundle 'scrooloose/syntastic' Bundle 'slim-template/vim-slim' Bundle 'thoughtbot/vim-rspec' Bundle 'tpope/vim-bundler' Bundle 'tpope/vim-endwise' Bundle 'tpope/vim-fugitive' Bundle 'tpope/vim-rails' Bundle 'tpope/vim-surround' Bundle 'vim-ruby/vim-ruby' Bundle 'vim-scripts/ctags.vim' Bundle 'vim-scripts/matchit.zip' Bundle 'vim-scripts/tComment' Bundle >"mattn/emmet-vim" Bundle >"scrooloose/nerdtree" Bundle >"Lokaltog/vim-powerline" Bundle >"godlygeek/tabular" Bundle >"msanders/snipmate.vim" Bundle >"jelera/vim-javascript-syntax" Bundle >"altercation/vim-colors-solarized" Bundle >"othree/html5.vim" Bundle >"xsbeats/vim-blade" Bundle >"Raimondi/delimitMate" Bundle >"groenewege/vim-less" Bundle >"evanmiller/nginx-vim-syntax" Bundle >"Lokaltog/vim-easymotion" Bundle >"tomasr/molokai" Bundle >"klen/python-mode&quot; 

打开vim,运行 :BundleInstall
或在shell中直接运行 vim +BundleInstall +qall

Install Bundle.png

常用插件

NERD Tree

NERD Tree是一个树形目录插件,方便浏览当前目录有哪些目录和文件。

NERD Tree Plugin Bundle.png

我在 ~/.vimrc文件中配置NERD Tree,设置一个启用或禁用 NERD Tree的键映射

nmap <f5> :NERDTreeToggle<cr> 

NERD Tree Configuration.png

所以你只需按 F5键就能启用或禁用 NERD Tree,NERD Tree提供一些常用快捷键来操作目录:

  • 通过 hjkl来移动光标
  • o打开关闭文件或目录,如果想打开文件,必须光标移动到文件名
  • t在标签页中打开
  • s和 i可以水平或纵向分割窗口打开文件
  • p到上层目录
  • P到根目录
  • K到同目录第一个节点
  • P到同目录最后一个节点

YouCompleteMe & syntastic

YouCompleteMe是一个快速、支持模糊匹配的vim代码补全引擎。由于它是基于 Clang引擎为C/C++/Objective-C提供代码提示,也支持其他语言代码提示的引擎,例如基于 Jedi的Python代码补全,基于 OmniSharp的C#代码补全,基于 Gocode的Go代码补全。

YouCompleteMe.gif

只需敲入代码,就自动提示想输入的代码列表,你可以选择其中一个,然后 tab键就可以补全代码。

YouCompleteMe已经集成了 Syntastic,所以一旦你编写代码时语法错误,就会有红色错误提示

syntastic.png

ctrlp

不知道你有没有遇到这样一种情况:在大规模的工程项目中,目录和文件嵌套比较深,打开一个文件要逐个逐个进入目录才能打开,这样的话,比较耗时间和效率很低, ctrlp重新定义打目录和文件方式,特别适用于大规模项目文件的浏览。

启用ctrlp

  • 运行命令 :CtrlP
    :CtrlP [starting-directory]
    来以查找文件模式来启用 ctrlp
  • 运行命令 :CtrlPBuffer
    :CtrlPMRU
    来以查找缓冲或最近打开文件模式来启用 ctrlp
  • 运行命令 CtrlPMixed
    来查找文件、查找缓冲和最近打开文件混合模式来启动 ctrlp

基本使用



  • 在三种查找模式中互相切换

  • 来创建新文件和对应的父目录

  • 来切换到只查找文件名而不是全路径


  • 或箭头方向键来移动查找结果列表



  • 来以新标签或分割窗口的方式来打开文件

  • 来标识或取消标识文件,然后按
    来打开文件


  • 来在提示历史中选择下一个/上一个字符串

演示视频

具体如何使用ctrlp,请参考happypetterd的

演示视频

,讲解非常清楚。

Vim Powerline

Vim Powerline是一个显示vim状态栏插件,它能够显示vim模式、操作环境、编码格式、行数/列数等信息

Vim Powerline.png

Molokai

Molokai是vim颜色主题,效果如下

Molokai Color Scheme for Vim.png

常用命令

对于入门vim基本命令可以参考简明 Vim 练级攻略,以下是本人关于 移动光标、 插入/修改、 删除、 复制、 粘帖、 撤销和恢复等常用命令

  • 移动光标

    1. 对于在 行内移动,通过使用 f/F + 字符
      来移动到特定的字符,然后再使用 .
      来重复执行命令; f
      表示向前移动, F
      表示向后移动。如果想直接移动到行首或行尾,使用 ^
      $
    2. 对于在 多行移动,就有多种选择: 第一种是通过 gg
      G
      行数 + G
      指定行数来移动, gg
      表示移动文件的第一行, G
      表示移动文件的最后一行, 行数 + G
      表示移动到特定的行。 第二种就是通过 正则搜索的方式来移动, /string
      表示正向查找, ?string
      表示反向查找, n
      查找下一个匹配的结果, N
      表示上一个匹配的结果,按 up/down
      可以浏览搜索历史。 第三种就是使用 标记来移动, m + {a-z}
      标记位置(适用于单个文件,如果是多个文件,使用大写字母 {A-Z}
      ), `{mark}
      移动到标记位置的列, '{mark}
      移动到标记位置的行首,还有一些特殊的标记, '
      表示跳转前光标的位置
  • 选择文本

    v
    不规则选择

    V
    按行选择

    Ctrl + V
    按列选择

  • 插入/修改

    i
    在当前字符前面插入

    I
    在行首插入

    a
    在当前字符后面插入

    A
    在行尾插入

    o
    在当前行的下一行插入

    O
    在当前行的上一行插入

    r
    更改当前的字符

    R
    更改多个字符

    cw/caw
    更改单词

    cf + 字符
    更改从当前字符到指定字符

    c$
    更改从当前字符到行尾

    cc
    更改整行

  • 删除

    x
    删除字符

    df + 字符
    删除从当前字符到指定字符

    dw/daw
    删除单词

    d$
    删除从当前光标到行尾

    dd
    删除一行

  • 剪切与粘帖

    dd + p
    delete一行,然后放在当前光标下方

    dd + P
    delete一行,然后放在当前光标上方

    dw + p
    delete单词,然后放在当前光标后面

    dw + P
    delete单词,然后放在当前光标前面

    p/P
    可接受计数前缀,重复粘贴

  • 复制

    yw
    复制单词

    yf
    复制从当前字符到指定字符

    y$
    复制当前光标到行尾

    yy
    复制整行

  • 撤销和恢复

    u
    撤销

    ctrl + r
    重做

  • 重复操作

    数字+action
    表示执行某个操作多少次

    .
    重复上一个操作

  • 宏录制

    q + 寄存器(a-z)
    开始录制

    录制动作
    >

    q
    停止录制

    @ + 寄存器 / @@replay被录制的宏

扩展阅读

原文:http://www.kuqin.com/shuoit/20150606/346467.html

vim: Vim – 适合自己的,才是最好的

Vim 被称为编辑器之神,是我用过之后才体会到的,用之前实在不敢对它做出什么评价。在大学时代,Vim 的大名就已如雷贯耳,但由于它陡峭的学习曲线,一直望而却步。等真正开始学习之后,发现并没有想象中的复杂,也没有所谓的瓶颈,只要在实际写代码中强迫自己使用就可以了,无形中就会形成习惯。最初的不适,换来的是效率的飞升。这和我当初学习双拼的感觉一样。下图是我的 Vim 界面:

vim: Vim - 适合自己的,才是最好的
vim: Vim – 适合自己的,才是最好的

学习方式

我一开始也是看了很多教程,这里我就不说具体的学习方法了,因为 Google 上一搜一大堆。

我只想谈一点:很多「过来人」告诫新手,一开始使用 Vim 一定不能使用插件, 要从最纯净的 Vim 开始练习。他们认为一上手就使用别人的配置,很容易被别人影响,不能领会到自己配置 Vim,这种从无到有的感觉。虽然我也很喜欢折腾的感觉,但这对于学习、入门一个工具来说有点 南辕北辙,我们学习一个工具就是为了用好它,或者 用它来为我们服务。为什么要我们去适应它呢?也许这不符合 Vim 的哲学,但是我觉得:

  • Vim 存在这么多年,已经有很多优秀的 Vim 配置(比如: spf13-vim),可以为我们节省很多折腾的时间。不过如果你非常喜欢折(zuo)腾(si),那也可以从头开始。
  • 对于新手来说,自己的配置总是很不成熟,到头来还是得参考一些高手的配置。索性一开始用他们的,慢慢删改。
  • 从纯净版开始你会觉得很枯燥,Vim 远没别人口中、视频中所述的酷炫,效率不升反降。这很容易丧失进阶的兴趣。
  • 天下武功,唯快不破,这个时代求快。我不否认先夯实基础,再层层递进的学习方式,但针对不同的学习对象,不同的环境背景,我们还是应该采取最快、最有效的学习方式。

如果你学习 Vim 是为了体验学习的新鲜感,或者业余玩味,请忽略我上面的话。但如果你的最终目的是为了在实际中用到它,提升我们的工作效率,那你不妨和我一样,直接拉别人的配置下来,在 Shell 里输入 Vim 启动,开始写代码!

当时我找到了 k-vim,按照他的安装步骤,很简单就把 Vim 配置好了,启动 Vim,发现界面也很漂亮,嗯,这就是我要的效果。接着,我打开自己那两天正在写的项目,通过仅会的四个快捷键 HJKL移动光标来查看文件。然后我仔细阅读了 k-vim的 README 文件,把它提到的几个快捷键试了试,感觉很不错。接下来的几天,它的 README 网页我一直开着,遇到想要的快捷键一搜就搞定,虽然写代码的效率确实下降了很多,但对编辑器的使用越来越纯熟。一周之后我已经习惯用 Vim 来编程了。

接下来开始进一步研究 Vim,理解 Vim 的 三种模式(正常模式、命令模式、视图模式),然后掌握如何配置 插件和 快捷键就OK了。最关键一点就是要实战,强迫自己所有的操作只用键盘,强迫只用 Vim 作编辑器。

插件与快捷键

Vim 的插件可以通过 Vundle来管理。(据说 vim-plug也挺好用)

只需两步:

  • vimrc.bundles文件中配置你想要的插件
  • 在 Vim 的命令模式中输入 :BundleInstall

其他的命令有:

:BundleUpdate    //更新插件
:BundleClean     //删除插件
 

个人觉得必备的插件:

  • syntastic 多语言语法检查
  • YouCompleteMe 代码自动补全
  • ctrlp.vim 文件搜索,类似 Sublime Text 里面的 Cmd + P
  • vim-airline 状态栏增强
  • nerdtree目录树
  • vim-ctrlspace tab/buffer导航增强

而快捷键的学习方法,就是用到的时候去 Google,多用几次就记住了。如果它自带的快捷键用着不舒服,你完全可以自己重设,Vim 就是自由,不必拘泥条条框框。

哲学

非常推荐阅读 Stack Overflow 上的这篇回答:

What is your most productive shortcut with Vim?

这篇真正阐述了 Vim 作者当初设计 Vim 快捷键时的哲学,看懂这篇对 Vim 快捷键的掌握会更上一层。

感悟

在学习 Vim、使用 Vim 的过程中,我最大的感悟就是 「适合自己的,才是最好的」。

很多插件看起来很酷炫,快捷键几下就能实现很繁杂的操作,但是你不一定会有使用这个插件的需求,或者即使用也用的不多。有人总喜欢拿 IDE 和 Vim 比,我觉得这根本没有比较的必要,你两个都用也没什么问题。大的项目,复杂的文件结构和引用,你不用 IDE 而用 Vim,是浪费时间。而且一般 IDE 都提供了 Vim 模式,你仍可以在 IDE 中继续击键如飞。

用 Vim 体验的是一种 轻便、自由、可塑的感觉。你可以根据自己的需求来培养 Vim,这就像恋(gao)爱(ji)一样是两个人互相适应的过程。互相习惯才能把效率最大化。

原文:http://www.geekplux.com/2015/06/06/vim-those-fit-yourself-are-the-best.html

vim: Vim插件学习

参考 @左耳朵耗子 的vim学习系列,不断补充中。

VIM万岁!:P

1. 浏览当前目录

Python

:E          浏览当前目录,回车进入目录或打开文件
    -       到上级目录
    D       删除文件
    R       改文件名
    s       对文件排序
    x       执行文件(无输出?) 

2. 编辑缓冲区

Python

:ls                     查看当前缓冲区打开文件(%指示当前缓冲区)
:buffer x               切换当前打开的文件x
    :bnext      (:bn)   后一个缓冲区文件
    :bprevious  (:bp)   前一个缓冲区文件
    :blast      (:bl)       最后一个缓冲区文件
    :bfirst     (:bf)       第一个缓冲区文件 

3. 窗口分屏浏览

Python

:Hexplore       (:He)   上下分屏,并在下屏浏览目录
:He!                    上下分屏,并在上屏浏览目录
:Vexplore       (:Ve)   左右分屏,并在左屏浏览目录
:Ve!                    左右分屏,并在右屏浏览目录
:set scb                设置分屏同步移动
:set scb!               解除分屏同步移动 

4. Tab浏览

Python

:Texplore       (:Te)   分Tab浏览目录
    gt                  移动到上一页
    gT                  移动到下一页
    {i} gt              移动到指定(i)页 

5. 光标移动

Python

%                       首先将光标移动至括号处 (、{、[ ,然后输入%即可将光标移动至与之相匹配的另一处
*, #                    将光标移动至某单词,通过(*下一个)或(#上一个)移动光标至所匹配的单词处
<start><command><end>   从start至end执行command
command:
    y                   拷贝
    d                   删除
    v                   可视化选择
    gU                  变大写
    gu                  变小写
position:
    0                   行头
    $                   行尾
    ^                   本行第一个非blank字符
    g_                  本行最后一个非blank字符
    fa                  到本行下一个“a”字符处
    t,                  到“,”前的第一个字符处 

6. 参考文献

原文:http://kevinsj.com/?p=347

vim: 从 0 开始搭建 Vim 编辑器

Abstract

本文主要介绍如何从0开始,构造适合自己的vim。目的不是比较什么编辑器更好,也不是宣传vim多么神奇,只是想给需要的人提供一些帮助。

文章以 ubuntu14.04, Vim 7.4为例子。主要介绍如何自己动手构造能高效编码的Vim,而不是使用已有的配置。这里说明,已有的配置固然很好,但是也是有针对性,初用Vim应该在探索中发现自己喜欢的配置。

远古时代

首先,原始的vi用起来是很别扭的。我新装了一个虚拟机,里面还没有vim,只有vi。

vim: 从 0 开始搭建 Vim 编辑器
vim: 从 0 开始搭建 Vim 编辑器

可以看到,刚装上的时候vi是相当之简陋。↑↓←→四个键都不能用。

从 Vi 升级到 Vim

sudo apt-get install vim

这里当然提前要把源设置好。如何设置源不是本文讨论的范围。

vim: 从 0 开始搭建 Vim 编辑器
vim: 从 0 开始搭建 Vim 编辑器

Vim安装完成之后,会自动覆盖vi。现在的vim已经是一个可以使用的版本了。忍不住来一段HelloWorld。

vim: 从 0 开始搭建 Vim 编辑器
vim: 从 0 开始搭建 Vim 编辑器

唉~~~~那么问题来了:

  1. 配色怎么这么难看。
  2. 怎么没有行号。
  3. 这个tab键一下子空了8格,能不能调一下…

.vimrc 的设置

首先介绍一下这个.vimrc文件。这个文件在根目录里面,ls查看不到,用ll可以看到。这个文件是vim的配置文件,想要解决上面的几个问题,我们需要更改这个文件里面的一些内容。

初次使用根目录中可能根夲就没有.vimrc文件。noproblem,可以自己建一个。

设置行号,用4空格代替tab

vi .vimrc

打开了一个空文件,我们先来加一点东西在里面。

set nu //加入行号显示
set ts=4 //将tab键宽度定义为4
set expandtab //用空格代替tab
 

保存退出。再次进入刚刚的.vimrc,行号出来了。按一下tab试试,4空格!YES!

设置colorscheme

颜色还是那么难看。没关系,这个是可以调的。首先,终端要改成用户自定义颜色。Edit->Profile_Proference

需要将这个对钩去掉。然后自己选一个想要的底色。

然后,在 usr/shared/vim/vim74/color里面,有很多配色方案。打开.vimrc,加入

colorscheme desert //此处desert可换成任意一种配色方案

再次启动vim。


好看了??- -|||

设置自动缩进

set cindent
set cinoptions={0,1s,t0,n-2,p2s,(03s,=.5s,>1s,=1s,:1s
 

将着两行也加入到.vimrc, 可以让代码以C风格缩进。编辑的时候不用频繁的敲tab和space了。

summary

上面简单介绍了vim的初步配置,但是,仅仅这样还是不够。你可能会喜欢Eclipse左边的文件树结构,可能也喜欢Eclipse可以显示一个类里面的所有方法,也可能喜欢Eclipse的静态语法检查,也可能喜欢Eclipse的……

怎么都是Eclipse! – -|||因为Eclipse实在太好用…

我想说的是,Eclipse有的功能,vim一样可以。要实现诸多的功能,vim需要安装一些插件。

Vim 插件管理

vim插件众多,配置插件更是一件麻烦事儿,不过没关系,推荐大家一款插件管理器 Vundle。有了Vundle,插件的安装不再麻烦。

当然,首先你要有一个Vundle。

Vundle安装

Vundle网址

参考里面的安装方式,首先你要装git,用git装Vundle。

没事,咱们就先装个git。

sudo apt-get install git

搞定!

下面可以安装Vundle了。

git clone https://github.com/gmarik/Vundle.vim.git ~/.vim/bundle/Vundle.vim
 

NOTE:如果https访问失败,可以改正git试一下。

好了,Vundle也有了。在我们安装我们喜欢的插件之前,要按照vbundle的说明书,自己看看 .v imrc里面需要怎么写。

请仔细阅读Vundle的说明书。就那么一页纸,please~~~

vim: 从 0 开始搭建 Vim 编辑器
vim: 从 0 开始搭建 Vim 编辑器

我们的.vimrc文件现在应该改成这个样子。想装的插件必须放在begin()和end()中间。其中这个Vundle.vim是必须要有的。

现在打开vim,在命令模式下

:PluginInstall

试一下。此时插件Vbundle正在安装。

vim: 从 0 开始搭建 Vim 编辑器
vim: 从 0 开始搭建 Vim 编辑器

安装完成后,左下角会出现Done!

此时我们已经可以安装自己想要的任何插件了。不过首先你要有插件 – -||||

NerdTree

NerdTree
可以让你的vim拥有文件树,just like Eclipse and vs。

ctrlp

ctrlp
可以方便的在vim打开文件。

summary

将上面两个插件的git地址,按照vundle要求的格式加入.vimrc。

vim: 从 0 开始搭建 Vim 编辑器
vim: 从 0 开始搭建 Vim 编辑器

还是按照上面的步骤,运行vim,执行

:PluginInstall
vim: 从 0 开始搭建 Vim 编辑器
vim: 从 0 开始搭建 Vim 编辑器

安装完成后,打开刚刚的HelloWorld。输入

:NERDTreeToggle

打开了文件树。

在命令模式下,ctrl-p,可以打开文件检索。

vim: 从 0 开始搭建 Vim 编辑器
vim: 从 0 开始搭建 Vim 编辑器

Summary

vim还有很多有意思的插件,有了这些插件,可以极大的提高我们的工作效率。

最后感觉,插件的并不是越多越好,也不是vim就是最好。写java裸体Eclipse跟全副武装的vim谁更好用不言自明。所以,针对不容的环境,不同的项目,应选择最合适的编辑器,就像不同的项目要用不同的开发语言一样。

原文:http://segmentfault.com/a/1190000002885785

Linux:使用条块化I/O管理多个LVM磁盘(第五部分)

在本文中,我们将了解逻辑卷是如何通过条块化I/O来写入数据到磁盘的。逻辑卷管理的酷炫特性之一,就是它能通过条块化I/O跨多个磁盘写入数据。

Linux:使用条块化I/O管理多个LVM磁盘(第五部分)
Linux:使用条块化I/O管理多个LVM磁盘(第五部分)

LVM条块化是什么?

LVM条块化是LVM功能之一,该技术会跨多个磁盘写入数据,而不是对单一物理卷持续写入。

Linux:使用条块化I/O管理多个LVM磁盘(第五部分)
Linux:使用条块化I/O管理多个LVM磁盘(第五部分)

使用条块化I/O管理LVM磁盘

条块化特性

  • 它会改善磁盘性能。
  • 避免对单一磁盘的不断的大量写入。
  • 使用对多个磁盘的条块化写入,可以减少磁盘填满的几率。

在逻辑卷管理中,如果我们需要创建一个逻辑卷,扩展的卷会完全映射到卷组和物理卷。在此种情形中,如果其中一个PV(物理卷)被填满,我们需要从其它物理卷中添加更多扩展。这样,添加更多扩展到PV中后,我们可以指定逻辑卷使用特定的物理卷写入I/O。

假设我们有四个磁盘驱动器,分别指向了四个物理卷,如果各个物理卷总计可以达到100 I/O,我们卷组就可以获得400 I/O

如果我们不使用条块化方法,文件系统将横跨基础物理卷写入。例如,写入一些数据到物理卷达到100 I/O,这些数据只会写入到第一个PV(sdb1)。如果我们在写入时使用条块化选项创建逻辑卷,它会分割100 I/O分别写入到四个驱动器中,这就是说每个驱动器中都会接收到25 I/O。

这会在循环过程中完成。如果这些逻辑卷其中任何一个需要扩展,在这种情形下,我们不能添加1个或2个PV,必须添加所有4个pv来扩展逻辑卷大小。这是条块化特性的缺点之一,从中我们可以知道,在创建逻辑卷时,我们需要为所有逻辑卷分配相同的条块大小。

逻辑卷管理有着这些特性,它使我们能够同时在多个pv中条块化数据。如果你对逻辑卷熟悉,你可以去设置逻辑卷条块化。反之,你则必须了解逻辑卷管理的基础知识了,请阅读更基础的文章来了解逻辑卷管理。

我的服务器设置

这里,我使用CentOS6.5用作练习。下面这些步骤也适用于RHEL、Oracle Linux以及大多数发行版。

操作系统:    CentOS 6.5
IP地址:     192.168.0.222
主机名:        tecmint.storage.com

条块化I/O的逻辑卷管理

出于演示目的,我已经准备了4个硬盘驱动器,每个驱动器1GB大小。让我用下面的‘fdisk’命令来列给你看看吧。

# fdisk -l | grep sd
Linux:使用条块化I/O管理多个LVM磁盘(第五部分)
Linux:使用条块化I/O管理多个LVM磁盘(第五部分)

列出硬盘驱动器

现在,我们必须为这4个硬盘驱动器sdbsdcsddsde创建分区,我们将用‘fdisk’命令来完成该工作。要创建分区,请遵从本文第一部分步骤#4的说明,并在创建分区时确保你已将类型修改为LVM(8e)

# pvcreate /dev/sd[b-e]1 -v
Linux:使用条块化I/O管理多个LVM磁盘(第五部分)
Linux:使用条块化I/O管理多个LVM磁盘(第五部分)

在LVM中创建物理卷

PV创建完成后,你可以使用‘pvs’命令将它们列出来。

# pvs

Verify Physical Volumes

验证物理卷

现在,我们需要使用这4个物理卷来定义卷组。这里,我定义了一个物理扩展大小(PE)为16MB,名为vg_strip的卷组。

# vgcreate -s 16M vg_strip /dev/sd[b-e]1 -v

上面命令中选项的说明:

  • [b-e]1 – 定义硬盘驱动器名称,如sdb1,sdc1,sdd1,sde1。
  • -s – 定义物理扩展大小。
  • -v – 详情。

接下来,验证新创建的卷组:

# vgs vg_strip
Linux:使用条块化I/O管理多个LVM磁盘(第五部分)
Linux:使用条块化I/O管理多个LVM磁盘(第五部分)

验证卷组

要获取VG更详细的信息,可以在vgdisplay命令中使用‘-v’选项,它将给出vg_strip卷组中所使用的全部物理卷的详细情况。

# vgdisplay vg_strip -v
Linux:使用条块化I/O管理多个LVM磁盘(第五部分)
Linux:使用条块化I/O管理多个LVM磁盘(第五部分)

卷组信息

回到我们的话题,现在在创建逻辑卷时,我们需要定义条块化值,就是数据需要如何使用条块化方法来写入到我们的逻辑卷中。

这里,我创建了一个名为lv_tecmint-strp1,大小为900MB的逻辑卷,它需要放到vg_strip卷组中。我定义了4个条块,就是说数据在写入到我的逻辑卷时,需要条块化分散到4个PV中。

# lvcreate -L 900M -n lv_tecmint_strp1 -i4 vg_strip
  • -L –逻辑卷大小
  • -n –逻辑卷名称
  • -i –条块化

Create Logical Volumes

创建逻辑卷

在上面的图片中,我们可以看到条块尺寸的默认大小为64 KB,如果我们需要自定义条块值,我们可以使用-I(大写I)。要确认逻辑卷已经是否已经创建,请使用以下命令。

# lvdisplay vg_strip/lv_tecmint_strp1
Linux:使用条块化I/O管理多个LVM磁盘(第五部分)
Linux:使用条块化I/O管理多个LVM磁盘(第五部分)

确认逻辑卷

现在,接下来的问题是,我们怎样才能知道条块被写入到了4个驱动器。这里,我们可以使用‘lvdisplay’和-m(显示逻辑卷映射)命令来验证。

# lvdisplay vg_strip/lv_tecmint_strp1 -m
Linux:使用条块化I/O管理多个LVM磁盘(第五部分)
Linux:使用条块化I/O管理多个LVM磁盘(第五部分)

检查逻辑卷

要创建自定义的条块尺寸,我们需要用我们自定义的条块大小256KB来创建一个1GB大小的逻辑卷。现在,我打算将条块分布到3个PV上。这里,我们可以定义我们想要哪些pv条块化。

# lvcreate -L 1G -i3 -I 256 -n lv_tecmint_strp2 vg_strip /dev/sdb1 /dev/sdc1 /dev/sdd1

Define Stripe Size

定义条块大小

接下来,检查条块大小和条块化的卷。

# lvdisplay vg_strip/lv_tecmint_strp2 -m
Linux:使用条块化I/O管理多个LVM磁盘(第五部分)
Linux:使用条块化I/O管理多个LVM磁盘(第五部分)

检查条块大小

是时候使用设备映射了,我们使用‘dmsetup’命令来完成这项工作。它是一个低级别的逻辑卷管理工具,它用于管理使用了设备映射驱动的逻辑设备。

# dmsetup deps /dev/vg_strip/lv_tecmint_strp[1-2]

Device Mapper

设备映射

这里,我们可以看到strp1依赖于4个驱动器,strp2依赖于3个设备。

希望你已经明白,我们怎样能让逻辑卷条块化来写入数据。对于此项设置,必须掌握逻辑卷管理基础知识。

在我的下一篇文章中,我将给大家展示怎样在逻辑卷管理中迁移数据。到那时,请静候更新。同时,别忘了对本文提出有价值的建议。


via: http://www.tecmint.com/manage-multiple-lvm-disks-using-striping-io/

作者:Babin Lonston 译者:GOLinux 校对:wxy

本文由 LCTT 原创翻译,Linux中国 荣誉推出

来源:https://linux.cn/article-4445-1.html

Linux:JVM的自愈能力

在IT行业,碰到问题的第一个反应通常是——“你重启过没”——而这样做可能会适得其反,本文要讲述的就是这样的一个场景。

Linux:JVM的自愈能力
Linux:JVM的自愈能力

接下来要介绍的这个应用,它不仅不需要重启,而且毫不夸张地说,它能够自我治愈:刚开始运行的时候它可能会碰到些挫折,但会渐入佳境。为了能实际地展示出它的自愈能力,我们尽可能简单地重现了这一场景,这个灵感还得归功于五年前heinz Kabutz发表的一篇老文章:

package eu.plumbr.test;
public class HealMe {
  private static final int SIZE = (int) (Runtime.getRuntime().maxMemory() * 0.6);
  public static void main(String[] args) throws Exception {
    for (int i = 0; i < 1000; i++) {
      allocateMemory(i);
    }
  }
  private static void allocateMemory(int i) {
    try {
      {
        byte[] bytes = new byte[SIZE];
        System.out.println(bytes.length);
      }
      byte[] moreBytes = new byte[SIZE];
      System.out.println(moreBytes.length);
      System.out.println("I allocated memory successfully " + i);
    } catch (OutOfMemoryError e) {
      System.out.println("I failed to allocate memory " + i);
    }
  }
}

 上述代码会循环地分配两块内存。每次分配的内存都是堆中总内存的60%。由于在同一个方法内会不停地进行这个内存分配,因此你可能会认为这段代码会 不断地抛出 java.lang.OutOfMemoryError: Java heap space异常,永远无法正常地执行完allocateMemory方法。

我们先来对源代码进行下静态分析,看看这种猜测是否恰当:

  1. 乍看一下这段程序的话,这确实是无法成功执行的,因为要分配的内存已经超出了JVM的限制。
  2. 但再仔细分析下的话我们会发现第一次分配是在一个块作用域内完成的,也就是说这个块中定义的变量仅对块内可见。这意味着这些内存在这个代码块执行完成后便可以回收掉了。这段代码一开始应该是可以成功执行的,只是当它再去尝试分配moreBytes的时候才会挂掉。
  3. 如果再查看下编译后的class文件的话,你会看到如下的字节码:
    private static void allocateMemory(int);
        Code:
           0: getstatic     #3                  // Field SIZE:I
           3: newarray       byte
           5: astore_1
           6: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
           9: aload_1
          10: arraylength
          11: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
          14: getstatic     #3                  // Field SIZE:I
          17: newarray       byte
          19: astore_1
          20: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
          23: aload_1
          24: arraylength
          25: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
    ---- cut for brevity ----

从中能够看出,第一个数组是在位置3~5处完成分配的,并存储到了序号为1的本地变量中。随后在位置17处,正要分配另一个数组。不过由于第一个数 组仍被本地变量所引用着,因此第二次分配总会抛出OOM的异常而失败。字节码解释器不会允许GC去回收第一个数组,因为它仍然存在着一个强引用。

从静态代码分析中可看出,由于底层的两个约束,上述的代码是无法成功执行的,而在第一种情况下则是能够运行的。这三点分析里面哪个才是正确的呢?我 们来实际运行下看看结果吧。结果表明,这些结论都是正确的。首先,应用程序的确无法分配内存。但是,经过一段时间之后(在我的Mac OS X上使用Java 8大概是出现在第255次迭代中),内存分配开始能够成功执行了:

 java -Xmx2g eu.plumbr.test.HealMe
1145359564
I failed to allocate memory 0
1145359564
I failed to allocate memory 1
… cut for brevity ...
I failed to allocate memory 254
1145359564
I failed to allocate memory 255
1145359564
1145359564
I allocated memory successfully 256
1145359564
1145359564
I allocated memory successfully 257
1145359564
1145359564
Self-healing code is a reality! Skynet is near...

 为了搞清楚究竟发生了什么,我们得思考一下,在程序运行期间发生了什么变化?显然,Just-In-Time编译开始介入了。如果你还记得的 话,JIT编译是JVM的一个内建机制,它可以优化热点代码。JIT会监控运行的代码,如果发现了一个热点,它会将你的字节码转化成本地代码,同时会执行 一些额外的优化,譬如方法内联以及无用代码擦除。

我们打开下面的命令行参数重启下程序,看看是否触发了JIT编译。

-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:+LogCompilation

 这会生成一个日志文件,在我这里是一个hotspot_pid38139.log文件,38139是Java进程的PID。在该文件中可以找到这么一行:

 这说明,在运行了256次allocateMemory()方法2之后,C1编译器决定将这个方法进行3级编译。看下这里可 以了解下分层编译的各个级别以及不同的阈值。在前面的256次迭代中这段程序都是在解释模式下运行的,这里的字节码解释器就是一个简单堆栈机器,它无法提 前预知某个变量后续是否会被用到,在这里对应的是变量bytes。但是JIT会一次性查看整个方法,因此它能推断出后面不会再用到bytes变量,可以对 它进行GC。所以才会触发垃圾回收,因此我们的程序才能奇迹般地自愈。我只是希望本文的读者都不要在生产环境碰到调试这类问题的情况。不过如果你想让某人 抓狂的话,倒是可以试试在生产环境中加下类似的代码。

来源:http://it.deepinmind.com/jvm/2014/12/15/self-healing-jvm.html