Qida's Blog

纸上得来终觉浅,绝知此事要躬行。

鉴于 ECMAScript 是松散类型(loosely typed)的,因此需要有一种手段来检测给定变量的数据类型——typeofinstanceof 操作符提供了这方面的信息:

typeof 操作符

typeof 操作符可能返回下列某个字符串:

类型字符串 描述
undefined 如果这个值未定义
boolean 如果这个值是布尔值
string 如果这个值是字符串
number 如果这个值是数值
object 如果这个值是对象或 null
function 如果这个值是函数

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
typeof undefined
"undefined"
typeof null
"object"
typeof true
"boolean"
typeof false
"boolean"
typeof ''
"string"
typeof ""
"string"
typeof 70
"number"
typeof 070
"number"
typeof 0xA
"number"
typeof 3.125e7
"number"
typeof 3e-7
"number"
typeof NaN
"number"
typeof Infinity
"number"

null

由于在检测对象的值时,typeof 无法辨别出 null 与对象,因此建议使用下列这样的判断:

1
2
3
4
var my_value = null;
if (my_value && typeof my_value === 'object') { // null 值为 false
// my_value 是一个对象或数组!
}

NaN

typeof 无法辨别出 NaN 和数字:

1
typeof NaN === 'number';    // true

isNaN() 函数可以解决这类判断问题:

1
isNaN(NaN);     // true

Infinity

typeof 无法辨别出 Infinity 和数字:

1
typeof Infinity === 'number';    // true

可以自定义一个 isNumber() 函数用于判断数字:

1
2
3
4
5
6
7
8
function isNumber(value) {
return typeof value === 'number'
&& isFinite(value); // isFinite 函数会筛选掉 NaN 和 Infinity
}

isNumber(100); // true
isNumber(NaN); // false
isNumber(Infinity); // false

function

比较特殊的类型是 function

1
2
typeof function(){}
"function"

从技术角度讲,函数在 ECMAScript 中是对象,而不是一种数据类型。然而,函数也确实有一些特殊的属性,因此通过 typeof 操作符来区分函数和其他对象是有必要的。

instanceof 操作符

typeof 操作符存在一个问题:在判断任何引用类型时都会返回 "object",因此 ECMAScript 引入了 instanceof 操作符来解决这个问题:

1
2
3
4
[] instanceof Array
true
new Date() instanceof Date
true

参考

  • 《JavaScript 高级程序设计》

Array

除了 Object 之外,Array 类型恐怕是 ECMAScript 中最常用的类型了。ECMAScript 的数组特点如下:

  • 数组是有序列表;
  • 数组的每一项可以保存不同类型的数据;
  • 数组的大小可以动态调整,可以随着数据的添加自动增长以容纳新增数据。

创建方式

创建 Array 实例的方式有两种。第一种是使用 Array 构造函数:

1
2
3
var colors = new Array();    // 创建一个空数组
var colors = new Array(20); // 创建 length 值为 20 的数组
var colors = new Array("red", "blue", "green"); // 创建一个包含 3 个字符串值的数组

另一种方式是使用数组字面量表示法:

1
2
var colors = [];    // 创建一个空数组
var colors = ["red", "blue", "green"]; // 创建一个包含 3 个字符串的数组

常用方法

针对数组有很多常用方法:

栈、队列方法

方法 描述
push() 入栈(向数组的末尾添加一个或更多元素,并返回新的长度)
pop() 出栈(删除并返回数组的最后一个元素)
unshift() 向数组的开头添加一个或更多元素,并返回新的长度。
shift() 出队(删除并返回数组的第一个元素)

重排序方法

方法 描述
sort() 按升序排列数组项
reverse() 反转数组项的顺序

操作方法

方法 描述
concat() 拼接并返回新数组
slice() 裁剪并返回新数组

拼接方法

方法 描述
join() 把数组的所有元素放入一个字符串。元素通过指定的分隔符进行分隔。
toString() 把数组转换为字符串,并返回结果。
toLocaleString() 把数组转换为本地字符串,并返回结果。

位置方法

ECMAScript 5 新增的方法:

方法 描述
indexOf() 查询特定项在数组的起始索引
lastIndexOf() 查询特定项在数组的结束索引

迭代方法

ECMAScript 5 新增的方法:

every()some() 是一组相似的方法,用于查询数组中的项是否满足某个条件:

方法 描述
every() 对数组中的每一项运行给定函数,如果该函数对每一项都返回 true,则返回 true
some() 对数组中的每一项运行给定函数,如果该函数对任一项返回 true,则返回 true
方法 描述
filter() 对数组中的每一项运行给定函数,返回该函数会返回 true 的项组成的数组。
map() 对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组。
forEach() 对数组中的每一项运行给定函数。这个方法没有返回值。

这些数组方法通过执行不同的操作,可以大大方便处理数组的任务。

归并方法

ECMAScript 5 新增的方法:

方法 描述
reduce() 从数组的第一项开始,逐个遍历到最后,执行给定的归并函数。
reduceRight() 从数组的最后一项开始,向前遍历到第一项,执行给定的归并函数。

参考

Object

ECMAScript 中使用最多的类型就是 Object。虽然 Object 的实例不具备多少功能,但对于在应用程序中存储和传输数据而言,它们是非常理想的选择。

创建方式

创建 Object 实例的方式有两种。第一种是使用 new 操作符后跟 Object 构造函数,如下所示:

1
2
3
var person = new Object();
person.name = "Nicholas";
person.age = 29;

另一种方式是使用对象字面量表示法。对象字面量是对象定义的一种简写形式,目的在于简化创建包含大量属性的对象的过程:

1
2
3
4
var person = {
name : "Nicholas",
age : 29
};

使用这种对象字面量语法要求的代码量更少,而且能够给人以封装数据的感觉。

实际上,对象字面量也是向函数传递大量可选参数的首选方式。一般来讲,命名参数虽然容易处理,但在有多个可选参数的情况下就会显示不够灵活。例如:

1
2
3
4
5
function doSomething(arg0, arg1, arg2, arg3, arg4) {  
...
}

doSomething('', 'foo', 5, [], false); // 这里必须传够五个命名参数,无法跳过中间某个可选参数

但最好的做法是对那些必需值使用命名参数,而使用对象字面量来封装多个可选参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function doSomething() {
// 不传任何参数也能正常运行
if (!arguments[0]) {
return false;
}

// 为 undefined 的参数设置默认值
var oArgs = arguments[0]
arg0 = oArgs.arg0 || "",
arg1 = oArgs.arg1 || "",
arg2 = oArgs.arg2 || 0,
arg3 = oArgs.arg3 || [],
arg4 = oArgs.arg4 || false;
}

// 传入可选参数而不报错
doSomething({
arg1: "foo",
arg2: 5,
arg4: false
});

属性和方法

由于在 ECMAScript 中 Object 是所有对象的基础,因此所有对象都具有下列这些基本的属性和方法:

属性 描述
constructor 保存着用于创建当前对象的函数。
isPrototypeOf(object) 用于检查传入的对象是否是传入对象的原型。
hasOwnProperty(propertyName) 用于检查给定的属性在当前对象实例中(而不是在实例的原型中)是否存在。
propertyIsEnumerable(propertyName) 用于检查给定的属性是否能够使用 for-in 语句来枚举。
toLocaleString() 返回对象的字符串表示,该字符串与执行环境的地区对应。
toString() 返回对象的字符串表示。
valueOf() 返回对象的字符串、数值或布尔值表示。通常与 toString() 方法的返回值相同。

例如,要检查某个对象的专有属性,可以使用 hasOwnProperty(propertyName) 方法进行判断:

1
2
3
var obj = {foo: 'foo', bar: 'bar'};
obj.hasOwnProperty('foo') // true
obj.hasOwnProperty('constructor') // false

参考

  • 《JavaScript 高级程序设计》
  • 《JavaScript 语言精粹》

尽管 ECMAScript 是一门弱类型语言,但它的内部提供了五种基本数据类型以便开发者使用。下面分别介绍:

基本数据类型

Undefined

在使用 var 声明变量但未对其加以初始化时,这个变量的值就是 undefined

Null

表示一个空对象指针。

String

String 类型用于表示 16 位 Unicode 字符组成的字符序列,即字符串。字符串可以由双引号("")或单引号('')表示,内含转义字符。

Boolean

虽然 Boolean 类型只有两个字面值——truefalse,但 ECMAScript 中所有类型的值都有与之等价的值。下表给出了转换规则:

转换规则

数据类型 转换为 true 的值 转换为 false 的值
Boolean true false
String 任何非空字符串 ""(空字符串)
Number 任何非零数字值(包括无穷大 Infinity 0NaN
Object 任何对象 null
Undefined 不适用 undefined

这些转换规则对于理解流控制语句(如 if 语句)、布尔操作符(!&&||)自动执行相应的 Boolean 转换非常重要,例如:

1
2
3
4
5
6
7
8
9
10
if('false') {console.log('true')}
true // 输出 true,因为进行了自动类型转换

window.hello; // undefined,因为该成员属性不存在
var foo = window.hello || 'unknown'; // 布尔操作符 || 可以用来填充默认值
foo; // 值为 'unknown'

window.hello.world; // 抛出 TypeError 异常,因为尝试从 undefined 的成员属性中取值
var bar = window.hello && window.hello.world; // 布尔操作符 && 可以用来避免该异常
bar; // 值为 undefined

Number

字面量

字面量 描述
70 十进制的 70。
-70 十进制的负 70。
070 八进制的 56。八进制字面值的第一位必须是零(0),然后是八进制数字序列(0~7)。
0xA 十六进制的 10。十六进制字面值的前两位必须是 0x,后跟任何十六进制数字(0~9A~F)。
3.125e7 科学计数法,表示“3.125 乘以 10 的 7 次幂(3.125 * Math.pow(10, 7))”,即 31250000。推荐使用这种简洁的方式来表示那些极大或极小的数值。
3e-7 科学计数法,表示 0.0000003。默认情况下,ECMASctipt 会将那些小数点后面带有 6 个零以上的浮点数值转换为以 e 表示法表示的数值。
Infinity 如果某次计算的结果得到了一个超出 ECMAScript 数值范围的值,那么该值将被自动转换成特殊的 Infinity 值。该值将无法继续参与下一次的计算,因为 Infinity 不是能够参与计算的数值。要想确定一个数值是不是有穷的(即是否位于最小和最大的数值之间),可以使用 isFinite() 函数进行判断。
NaN 非数值(Not a Number)是一个特殊的数值,用于表示一个本来要返回数值的操作数未返回数值的情况(这样就不会抛出错误了)。ECMAScript 定义了 isNaN() 函数,接收任何类型参数并(调用 Number() 函数)进行 自动类型转换,如果转换失败则这个参数“不是数值”。

浮点数值

Number 类型使用 IEEE754 格式来表示整数和浮点数值。这种格式有个通病:浮点数值计算会产生 舍入误差 的问题,从而导致无法测试 特定的 浮点数值,例如:

1
2
3
if (a + b == 0.3) { // 不要做这样的浮点测试!例如 0.1 加 0.2 的结果不是 0.3,而是 0.30000000000000004。
alert("You got 0.3.");
}

可见,浮点数值的最高精度虽然有 17 位小数,但在进行算术计算时其精确度远远不如整数。因此建议先将浮点数值转换成整数值进行计算后,再转回浮点数,如此一来就能缓解这个问题。

此外,由于保存浮点数值需要的内存空间是保存整数值的 两倍,因此 ECMAScript 会不失时机地将浮点数值转换为整数值,例如:

1
2
var floatNum1 = 1.;    // 小数点后面没有数字——解析为 1
var floatNum2 = 10.0; // 浮点数值本身表示整数——解析为 10

数值范围

由于内存限制,ECMAScript 并不能保存世界上所有的数值,其限制范围下表:

常量 描述
Number.MIN_VALUE ECMAScript 能够表示的最小数值,大多数浏览器中为 5e-324
Number.MAX_VALUE ECMAScript 能够表示的最大数值,大多数浏览器中为 1.7976931348623157e+308

如果某次计算的结果得到了一个超出 ECMAScript 数值范围的值,那么该值将被自动转换成特殊的 Infinity 值。该值将无法继续参与下一次的计算,因为 Infinity 不是能够参与计算的数值。要想确定一个数值是不是有穷的(即是否位于最小和最大的数值之间),可以使用 isFinite() 函数进行判断。

数值转换

有 3 个函数可以把非数值转换为数值:Number()parseInt()parseFloat()。第一个函数可以用于任何数据类型,而另两个函数则专门用于把字符串转换成数值。
但由于 Number() 函数在转换字符串时比较复杂而且不够合理,因此更常用的是 parseInt()parseFloat() 函数。

parseInt() 函数在转换字符串时,更多的是看其是否符合数值模式:

1
2
3
4
5
var num1 = parseInt('  70');       // 70(忽略字符串前面的空格,直至找到第一个非空格字符)
var num2 = parseInt('blue'); // NaN(如果第一个字符不是数字字符或者负号,返回 NaN)
var num3 = parseInt(""); // NaN(转换空字符串,也返回 NaN)
var num4 = parseInt("1234blue"); // 1234(解析直至遇到一个非数字字符)
var num5 = parseInt(22.5); // 22(小数点并不是有效的数字字符)

如果字符串中的第一个字符是数字字符,parseInt() 也能够识别出各种整数格式:

1
2
3
var num6 = parseInt("70");         // 70(十进制数)
var num5 = parseInt("070"); // 存在分歧,ECMAScript 3 认为是 56 (八进制),ECMAScript 5 认为是 70 (十进制)
var num3 = parseInt("0xA"); // 10(十六进制数)

为了消除在使用 parseInt() 函数时可能导致的上述困惑,可以为这个函数提供第二个参数:转换时使用的基数(即多少进制):

1
2
3
4
var num1 = parseInt("10", 2);    // 2 (按二进制解析)
var num2 = parseInt("10", 8); // 8 (按八进制解析)
var num3 = parseInt("10", 10); // 10 (按十进制解析)
var num4 = parseInt("10", 16); // 16 (按十六进制解析)

多数情况下,我们要解析的都是十进制数值,因此始终将 10 作为第二个参数是非常必要的。

基本包装类型

为了便于操作基本类型值,ECMAScript 还提供了以下 3 个特殊的引用类型,它们都具有与各自的基本类型相应的特殊行为。实际上,每当读取一个基本类型值的时候,后台就会隐式地创建一个对应的基本包装类型的对象,从而让我们能够调用一些实用方法来操作这些数据。

字面量 包装方法 实用方法
truefalse Boolean()
70 十进制
070 八进制
0xA 十六进制
3.125e7 科学计数法
Number() toFixed(fractionDigits) 按照指定的小数位四舍五入
toExponential(fractionDigits) 科学计数法
toPrecision(precision)
toString(radix) 使用指定基数(即多少进制)将数字转换为字符串
""'' String() charAt()
concat()
substring()
indexOf()
toLowerCase()
match()
……

不建议显式地创建基本包装类型的对象,因为会造成 typeof 操作符判断不符合预期:

1
2
3
4
5
6
typeof new Boolean(true)
"object"
typeof new Number(70)
"object"
typeof new String('')
"object"

快速类型转换

最后是一些类型转换的小技巧:

1
2
3
4
5
6
var myVar   = "3.14159",
str = ""+myVar, // to string
int = ~~myVar, // to integer
float = 1*myVar, // to float
bool = !!myVar, // to boolean - any string with length and any number except 0 are true
array = [myVar]; // to array

参考

一个完整的 JavaScript 实现由下列三个不同的部分组成:

1
2
3
4
5
6
7
8
9
+--------------------------+
| |
| JavaScript |
| |
| +----------+ +---+ +---+ |
| |ECMAScript| |DOM| |BOM| |
| +----------+ +---+ +---+ |
| |
+--------------------------+

下面分别介绍这些部分:

核心(ECMAScript)

以网景的 Netscape Navigator 内置的 JavaScript 1.1 为蓝本,由 ECMA-262 定义的 ECMAScript 是一种 与 Web 浏览器没有依赖关系 的脚本语言标准,它由下列基础部分组成:

  • 语法(Syntax)
  • 类型(Types)
  • 语句(Statements)
  • 关键字(Keywords)
  • 保留字(Reserved words)
  • 操作符(Operators)
  • 对象(Objects)

宿主环境

ECMA-262 定义的只是这门语言的基础部分,而在此基础之上,宿主环境(host environment) 可以构建更完善的脚本语言。常见的宿主环境有:

  • Web Broswer
  • Node.js
  • Adobe Flash

以我们最常见的 Web Broswer 为例,不仅提供了基本的 ECMAScript 实现,同时还提供了该语言的扩展,以便语言与环境之间对接交互。而这些扩展——如 DOM,则利用 ECMAScript 的核心类型(Types)和语法(Syntax)提供更多更具体的功能,以便实现针对环境的操作。

版本

ECMA-262 目前已经发布了六个大版本的 ECMAScript:

版本 发布时间 描述
1 1997 年 6 月 以网景的 Netscape Navigator 内置的 JavaScript 1.1 为蓝本制定,但删除了所有针对浏览器的代码,并支持 Unicode 标准(从而支持多语言开发)。
2 1998 年 6 月 基本没有修改。
3 1999 年 12 月 标准的第一次大修改,涉及:新增的正则表达式,更好的字符串处理,新的控制语句,try / catch 异常处理的支持,更严格的错误定义,数值格式化输出和其它增强功能。该版标志着 ECMAScript 成为了一门真正的编程语言。
4 已废弃 该版对 ECMAScript 进行了大刀阔斧的修改,但由于复杂的语言政治分歧而被废弃了。
5 2009 年 12 月 澄清了第三版规范许多模糊之处,并增加了一些新功能,如:原生 JSON 对象、继承的方法和高级属性定义,以及“严格模式(strict mode)”。是目前浏览器兼容性最好、最主流的版本。
5.1 2011 年 6 月 基本没有修改。
6 2015 年 6 月 标准的又一次大修改,被称为 ECMAScript 2015。它为编写日益复杂的应用程序增加了大量重要的新语法,包括:类(classes)和模块(modules)、新的迭代器(iterators)和 for/of 循环(loops)、Python 风格的生成器(generators)和生成器表达式、arrow functions, binary data, typed arrays, collections (maps, sets and weak maps), promises, number and math enhancements, reflection, and proxies …
更多特性详见这里
7 制定中

兼容

各个浏览器对 ECMAScript 5 的兼容性可查看 这里

文档对象模型(DOM)

文档对象模型(DOM,Document Object Model)是针对 XML 但经过扩展用于 HTML 的 API。借助 DOM 提供的 API,开发人员可以轻松自如地删除、添加、替换或修改任何节点,获得控制页面内容和结构的主动权。

浏览器对象模型(BOM)

浏览器对象模型(BOM,Browser Object Model)是一组浏览器提供的自定义扩展 API,可以控制浏览器显示的页面以外的部分,例如:

  • 弹出新浏览器窗口的功能;
  • 移动、缩放和关闭浏览器窗口的功能;
  • 提供浏览器详细信息的 navigator 对象;
  • 提供浏览器所加载页面的详细信息的 location 对象;
  • 提供用户显示器分辨率详细信息的 screen 对象;
  • 对 cookies 的支持;
  • 像 XMLHttpRequest 和 IE 的 ActiveXObject 这样的自定义对象。

常用的 BOM API 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
window
|
+--> document
|
+--> location
|
+--> navigator
|
+--> screen
|
+--> history
|
+--> ...

由于没有 BOM 标准可以遵循,因此每个浏览器都有自己的实现。虽然也存在一些事实标准,例如要有 window 对象和 navigator 对象等,但每个浏览器都会为这两个对象乃至其它对象定义自己的属性和方法。如今 HTML 5 致力于把很多 BOM 功能纳入正式规范,BOM 实现的细节有望朝着兼容性越来越高的方向发展。

参考

从 Windows 转到 Linux 的初学者,往往对 Linux 的目录结构感到无所适从。本文介绍的 FHS 标准,是理解这些目录结构的关键。

FHS 标准

FHS(Filesystem Hierarchy Standard,文件系统层次结构标准)采用树形结构组织文件,并定义了 Linux 系统中主要目录的用途、所需要的最小构成的文件和目录,同时还给出了例外处理与矛盾处理。多数 Linux 版本采用这种目录组织形式,类似于 Windows 操作系统中 C 盘的文件目录。

事实上,FHS 针对目录树结构仅定义出两层目录(//usr/var)底下应该放置什么数据,下面分别介绍这些目录:

/ (root)

在 FHS 标准中,所有的文件和目录都必须出现在根目录 / 下,即使它们存储在不同的存储设备或网络主机中。此外还要求根目录 / 下必须要有以下目录或符号链接(symbolic links):

目录 描述 备注
/etc Host-specific system configuration 系统配置文件
/dev Device files 设备文件
/bin Essential command binaries (for use by all users) 重要的执行文件
/sbin Essential system binaries (for use by root) 重要的系统执行文件
/lib Essential shared libraries and kernel modules 执行文件所需的函数库与内核所需的模块。/bin/sbin 中二进制文件必要的函数库
/boot Static files of the boot loader (include kenerl file、drivers) 系统开机文件
/media Mount point for removeable media
/mnt Mount point for mounting a filesystem temporarily (include hard disk、U disk、CD、DVD…)
/opt Add-on application software packages
/srv Data for services provided by this system
/tmp Temporary files 临时文件

注意:

  • 由于根目录 /开机、还原、系统修复等操作有关,而开机过程中仅有根目录会被挂载,其它分区则是在开机完成之后才会持续进行挂载,因此,根目录下与开机过程有关的目录(即上表前六个目录)不能够与根目录分开到不同分区。
  • 由于 FHS 的目录结构已经提供了足够的灵活性,因此标准要求,应用程序禁止在根目录下创建新的子目录,理由如下:
  • 这会额外占用根目录所在分区的空间,但系统管理员基于性能与安全考虑,会希望保持该分区小而简(small and simple);
  • It evades whatever discipline the system administrator may have set up for distributing standard file hierarchies across mountable volumes.

/usr (unix software resource)

该目录与软件安装/执行有关。

  • FHS 建议所有软件开发者,应该将他们的数据合理的分别放置到这个目录下的子目录,而不要自行建立该软件自己独立的目录
  • 由于所有系统默认的软件(distribution 发布者提供的软件)都会放置到 /usr 下,因此这个目录有点类似 Windows 系统的 C:\Windows\C:\Program files\ 这两个目录的综合体,系统刚安装完毕时,这个目录会占用最多的硬盘容量。
  • 用户后续安装的应用程序,也建议放置到 /usr 下。

/var (variable)

该目录与系统运作过程有关。

如果 /usr 是系统安装时会占用较大硬盘容量的目录,那么 /var 则是在系统运行时才会渐渐占用硬盘容量的目录。 /var 目录主要针对常态性变动的文件,包括缓存(cache)、登录文件(log file)以及某些软件运行所产生的日志文件,因此这个目录会越来越大,建议单独挂载分区。

总结

由于根目录所在分区的容量有限,因此像 /usr/var/home 这种大目录最好不要与根目录放在同一个分区内,而是建议单独挂载分区。如此一来不但可以提高系统性能,根目录所在的文件系统也不容易发生问题。

参考

Unix目录结构的来历
Linux 的文件权限与目录配置
FHS 官方文档
FHS 2.3 官方文档

续上文。

GNU/CoreUtils 的 Text utilities 提供了一些便利的文本处理命令,配合“管道”组合使用可以大大提高文本处理效率。

命令

下面介绍一些最常用的利用管道进行组合的命令:

grep

grep

grep 命令使用正则表达式以行为单位进行文本搜索(global search regular expression(RE) and print out the line),其命令格式如下:

1
grep [选项] 'PATTERN' [文本文件]

常用选项:

选项 描述 例子
-c, --count 打印匹配的行数
-m NUM, --max-count=NUM 打印前 NUM 行
-n, --line-number 打印行号
-v, --revert-match 反转查找(非)
-A , --after 可加数字,表示打印后面n行
-B , --before 可加数字,表示打印前面n行
-i 不区分大小写
-r 递归目录查找 grep -r "XXX" *
--color 关键字高亮
-E 使用正则表达式,替代 egrep 命令
-F 在一个或多个文件中搜索固定的字符串,替代 fgrep 命令

且(AND):

1
2
# 使用管道符连接多个 grep 命令
grep 'pattern1' filename | grep 'pattern2'

或(OR):

1
2
3
4
5
6
7
8
# 方法一:使用转义字符 \|
grep 'pattern1\|pattern2' filename

# 方法二:使用正则表达式选项 -E
grep -E 'pattern1|pattern2' filename

# 方法三:egrep 相当于 grep -E
egrep 'pattern1|pattern2' filename

非(NOT):

1
grep -v 'pattern' filename

参考:

https://stackoverflow.com/questions/9969414/always-include-first-line-in-grep

tr

tr 命令用于替换或删除指定的字符(注意不接收文件参数),其命令格式如下:

1
tr [options] string1 string2

可用于将小写转换成大写:

1
2
$ echo 'abcdef' | tr 'a-z' 'A-Z'
ABCDEF

-d 参数可用于删除指定的字符:

1
2
$ echo 'abcdef' | tr -d 'def'
abc

-s 参数可用于删除所有重复出现字符序列,只保留第一个;即将重复出现字符串压缩为一个字符串:

1
2
$ echo 'abbbbbbbbbc' | tr -s 'b'
abc

-d-s 常用于删除所有换行符 \n 和合并空格 [:space:]

1
$ cat logfile | tr -d '\n\t' | tr -s [:space:]

cut

cut 命令以行为单位,用于截取某段数据,如字节、字符和字段。其命令格式如下:

1
cut [选项] [范围] [文本文件]

使用 -d 指定分隔符(默认为制表符),例如:cut -d ':' -f -2 /etc/passwd

常用的几种选项如下:

选项 描述
-f, --fields 以字段为单位
-c, --characters 以字符为单位
-b, -- bytes 以字节为单位

常用的几种范围如下:

选项 描述
n 第 n 个
n- 从第 n 个到最后一个
n-m 从第 n 个到第 m 个
-m 从第一个到第 m 个
- 从第一个到最后一个
n,m 第 n、m 个

注意,在 UTF-8 编码下,汉字占三个字节。

sort

sort&uniq

sort 命令以行为单位,用于对文本文件内容进行排序。其命令格式如下:

1
sort [选项] [文本文件]

常用的选项如下:

选项 描述
-n, --numeric-sort, --sort=numeric 依照数值的大小排序(默认是以文字)
-r, --reverse 反向排序

uniq

uniq 命令以行为单位,用于合并文本文件中重复出现的行列。它比较相邻的行,在正常情况下,第二个及以后更多个重复行将被删去,因此在合并前常常会先使用 sort 命令排序。行比较是根据所用字符集的排序序列进行的。其命令格式如下:

1
uniq [选项] [文本文件]

常用的选项如下:

选项 描述
-i, --ignore-case Case insensitive comparison of lines.
忽略大小写
-c, --count Precede each output line with the count of the number of times the line occurred in the input, followed by a single space.
进行计数
-d, --repeated Output a single copy of each line that is repeated in the input.
只显示重复行(交集)
-u, --unique Only output lines that are not repeated in the input.
只显示不重复的行(差集)

例子:

1
2
3
4
5
cat a b | sort | uniq > c   # c is a union b

cat a b | sort | uniq -d > c # c is a intersect b

cat a b | sort | uniq -u > c # c is set difference a - b

wc

wc 命令用于统计字节数、字数、行数,其命令格式如下:

1
wc [选项] [文本文件]

常用的选项如下:

选项 描述
-l, --lines 只显示行数
-w, --words 只显示字数
-c, --chars 或 --bytes 只显示字节数

tee

tee 是一种双向重定向命令,用于可以将数据流处理过程中的某段结果保存到文件,其处理过程如下:

tee

常用的选项如下:

选项 描述
-a, --append 附加到既有文件的后面,而非覆盖它
-i, --ignore-interrupts 忽略中断信号

xargs

xargs

例子

1、统计 Nginx 独立 IP 数:

1
$ cut -d " " -f 1 nginx_log | sort | uniq | wc –l

2、统计当前用户最常用的 10 条命令:

1
$ cut -d " " -f 1 ~/.bash_history | sort | uniq -c | sort -nr | head

3、统计重复行,逆序方式:

1
$ sort /data/tradehistory_20150804.txt | uniq -cd | sort -nr

4、统计多个文件:

1
2
3
4
$ cat /data/tradehistory_2015080*.txt | cut -d ',' -f 13 | sort | uniq -c | sort -nr
29549 20150803
24086 20150805
19520 20150804

GNU/CoreUtils 是一组类 Unix 操作系统所需的基础软件包。它包含三组命令,常用的命令如 catlsrm。学习 GNU/Linux 的第一步,就是要熟悉软件包下常用的命令。下面分别介绍这三组常用的命令:

File utilities

Basic operations

命令 描述 备注
cp Copy files and directories cp -rp 备份目录。
-r 递归复制目录,否则提示“略过目录‘xxx’”。
-p 保留源文件或目录的属性(包括属主、属组、权限、修改时间等)。
-f 强制覆盖。
mv Move (rename) files
rm Remove files or directories rm -rf 强制递归删除文件或目录。
-r 递归删除,将指定目录下的所有文件及子目录一并处理。
-f 强制删除文件或目录。
ln Create a link to a file ln -s TARGET LINK_NAME 创建软链接。
mkdir Create a directory -p 递归创建目录。
rmdir Remove empty directories -p 递归删除空目录,如果目录非空会删除失败并提示:rmdir: failed to remove 'xxx': Directory not empty

Directory listing

命令 描述 备注
ls List directory contents -l 查看详细信息。
-a 显示隐藏文件。
-d 仅列出目录本身,而不是列出目录内的文件。列出所有子目录本身:ls -d */
-h 将文件容量以人类较易读的方式(如GB、KB等)列出来。
-t 按时间排序显示,默认为新的排在前面。
-S 按文件容量大小排序,而不是用文件名。
dir List directory contents briefly Exactly like ls -C -b
vdir List directory contents verbosely Exactly like ls -l -b

Changing file attributes

命令 描述 备注
chown Change file owner and group chown -R owner:group /there/is/a/file
-R 递归修改,常用于一次性更改某一目录内所有的文件、目录。目标属主必须在 /etc/passwd
chgrp Change group ownership -R 递归修改,目标属组必须在 /etc/group
chmod Change access permissions chmod [ugoa...][[+-=][rwxX]...][,...]
可用:ugoa,可用权限:r=4w=2x=1
例如:chmod u+x
touch Change file timestamps 改变文件访问和修改时间,也可用于快速创建一个文件。

Disk usage

命令 描述 备注
df Show disk free space on file systems -h 以 K,M,G 为单位,更易读的方式显示。
-i list inode information instead of block usage
du Show disk usage on file systems -h 以 K,M,G 为单位,更易读的方式显示。
-s, --summarize 汇总显示(等于 --max-depth=0
-d, --max-depth=N 显示第 N 层子目录各自的大小,常用于找出最占空间的目录。例如:du --max-depth=1 -h ./
--exclude=PATTERN Exclude files that match PATTERN.
stat Return data about an inode
truncate Shrink or extend the size of a file to the specified size -s 参数指定一个大小:K, M, G, T, P, E, Z, Y

disk usage

Text utilities

Output of entire files

命令 描述 备注
cat Concatenates and prints files on the standard output 常用于连接并输出多个文件的内容。
tac Concatenates and prints files on the standard output in reverse 常用于反向连接并输出多个文件的内容。
nl Numbers lines of files -b 指定行号的方式,主要有 a t两种:
-b a 无论是否是空行,同样列出行号。
-b t 默认值,不列出空行行号。
basenc Encode/decode data and print to standard output --base64 same as base64 program (RFC4648 section 4)
--base64url file- and url-safe base64 (RFC4648 section 5)
--base32 same as base32 program (RFC4648 section 6)
--base32hex extended hex alphabet base32 (RFC4648 section 7)
--base16 hex encoding (RFC4648 section 8)
-d, --decode decode data
base64 Encodes or decodes Base64, and prints result to standard output
base32 Encodes or decodes Base32, and prints result to standard output

cat

https://en.wikipedia.org/wiki/More_(command)

https://en.wikipedia.org/wiki/Less_(Unix)

less

Output of parts of files

命令 描述 备注
head Output the first part of files 默认输出 10 行
tail Output the last part of files -n 输出倒数 n 行(默认输出 10 行)
-f 不停读取输出文件的最新内容,常用于实时监视日志输出,用 Ctrl+C 来终止。
tailf 等同于 tail -f -n 10
split Split a file into pieces 用于按行、按大小分割文件
csplit Split a file into context-determined pieces

head&tail

Operating on sorted files

命令 描述 备注
sort Sort text files 详见本文
shuf Shuffling text
uniq Uniquify files 详见本文

Operating on fields

命令 描述 备注
cut Print selected parts of lines 详见本文
paste Merge lines of files 合并多个文件的所有行
join Joins lines of two files on a common field 合并两个文件中相同位置的行

Operating on characters

命令 描述 备注
tr Translate or delete characters 详见本文
expand Convert tabs to spaces
unexpand Convert spaces to tabs

Summarizing files

命令 描述 备注
wc Print the number of bytes, words, and lines in files 详见本文
sum Checksums and counts the blocks in a file
cksum Checksums (IEEE Ethernet CRC-32) and count the bytes in a file
b2sum Computes and checks BLAKE2b message digest
md5sum Computes and checks MD5 message digest
sha1sum
sha224sum
sha256sum
sha384sum
sha512sum
Computes and checks SHA-1/SHA-2 message digests

https://en.wikipedia.org/wiki/Checksum

A checksum is a small-sized block of data derived from another block of digital data for the purpose of detecting errors that may have been introduced during its transmission or storage.

checksum

https://en.wikipedia.org/wiki/Cryptographic_hash_function

单向散列算法

digest

Shell utilities

User information

命令 描述 备注
id Print user identity 显示当前用户的信息(uid、gid、groups)
logname Print current login name
whoami Print effective user ID
groups Print group names a user is in
users Print login names of users currently logged in
who Print who is currently logged in

System context

命令 描述 备注
date Print or set system date and time date +%Y-%m-%d 2016-12-28
arch Print machine hardware name
nproc Print the number of available processors
uname Print system information
hostname Print or set system name
hostid Print numeric host identifier
uptime Print system uptime and load 常用于查看系统负载

Working context

命令 描述 备注
pwd Print working directory 显示当前所在目录
stty Print or change terminal characteristics
tty Print file name of terminal on standard input
printenv Print all or some environment variables

Modified command invocation

命令 描述 备注
nohup Run a command immune to hangups
timeout Run a command with a time limit
env Run a command in a modified environment

Process control

命令 描述 备注
kill Send a signal to processes

Delaying

命令 描述 备注
sleep Delay for a specified time

Redirection

命令 描述 备注
tee Redirect output to multiple files or processes 详见本文

Conditions

命令 描述 备注
false Do nothing, unsuccessfully
true Do nothing, successfully
test Check file types and compare values
expr Evaluate expressions

Printing text

命令 描述 备注
echo Print a line of text -n Do not print the trailing newline character \n.
-e 开启转义字符,例如:反斜杠 \\、换行符 \n
printf Format and print data
yes Print a string until interrupted

yes 命令小技巧,使用管道自动输入“y”进行文件强制覆盖,方法:yes | cp 源文件 目的文件

Numeric operations

命令 描述 备注
seq Print numeric sequences
numfmt Reformat numbers 常用于格式化数字

File name manipulation

命令 描述 备注
basename Strip directory and suffix from a file name 截取出文件名
dirname Strip last file name component 截取出目录名

zip 压缩文件查看命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
zip -rmq dist.zip dist

-q 表示不显示压缩进度状态
-r 表示子目录子文件全部压缩为zip;这部分比较重要,不然的话只有something这个文件夹被压缩,里面的没有被压缩进去
-e 表示你的压缩文件需要加密,终端会提示你输入密码的;还有种加密方法,这种是直接在命令行里做的,比如zip -r -P Password01! modudu.zip SomeDir, 就直接用Password01!来加密modudu.zip了
-m 表示压缩完删除原文件

unzip [选项] dist.zip

-x 文件列表 解压缩文件,但不包括指定的file文件。
-v 查看压缩文件目录,但不解压。
-t 测试文件有无损坏,但不解压。
-d 目录 把压缩文件解到指定目录下。
-z 只显示压缩文件的注解。
-n 不覆盖已经存在的文件。
-o 覆盖已存在的文件且不要求用户确认。
-j 不重建文档的目录结构,把所有文件解压到同一目录下。
命令 描述 备注
zcat
zless
zmore
zgrep
zdiff

参考

GSLB(Global Server Load Balance,全局负载均衡)作为 CDN 系统架构中最核心的部分,负责流量调度。本文站在服务提供方的视角,做一些技术总结。

GSLB 横向对比

下表是三种常见的实现方式对比:

比较项 基于 DNS 解析方式 基于 HTTP 重定向方式 基于 IP 路由方式
性能 本地 DNS 服务器和用户终端 DNS 缓存能力使 GSLB 的负载得到有效分担 GSLB 处理压力大,容易成为系统性能的瓶颈 借助 IP 网络设备完成负载均衡,没有单点性能瓶颈
准确度 定位准确度取决于本地 DNS 覆盖范围,用户的本地 DNS 设置错误会造成定位不准确 在对用户 IP 地址数据进行有效维护的前提下,定位准确且精度高 就近性调度准确,但对设备健康性等动态信息响应会有延迟
效率 效率约等于 DNS 系统本身处理效率 依靠服务器做处理,对硬件资源的要求高 效率约等于 IP 设备本身效率
扩展性 扩展性和通用性好 扩展性较差,需对各种应用协议进行定制开发 通用性好,但适用范围有限
商用性 在 Web 加速领域使用较多 国内流媒体 CDN 应用较多 尚无商用案例

其中,基于 DNS 解析方式的 GSLB 有两个注意点:

准确度

本地 DNS 服务器(英文:Local DNS Server,缩写:LDNS)是用户所在局域网或 ISP 网络中使用的域名服务器,定位准确度就取决于它了。因为当用户在浏览器里访问某个域名时,浏览器会首先向 LDNS 发起查询,LDNS 再代为向整个 DNS 域名系统发起查询,直到找到解析结果。域名解析流程详见本文

如果 LDNS 设置不当,例如没有使用当前 ISP 提供的当地 LDNS,如 8.8.8.8,这种实现方式可能会误判用户的位置,从而将用户误导到错误的 CDN 缓存节点,造成加速效果差的问题。

缓存

DNS 的查询机制给使用它的互联网应用带来额外的时延,有时时延还比较大,为了解决问题,引入了“缓存”机制。缓存是指 DNS 查询结果在 LDNS 中缓存,当其它主机向它发起查询请求时,它就直接向主机返回缓存中能够找到的结果,直到数据过期。

在基于 DNS 解析方式下无论采用何种工作方式,都会有一些请求不会到达 GSLB,这是 DNS 系统本身的缓存机制在起作用。当用户请求的域名在本地 DNS 或本机(客户端浏览器)得到了解析结果,这些请求就不会达到 GSLB。Cache 更新时间越短,用户请求到达 GSLB 的几率越大。由于 DNS 的缓存机制屏蔽掉相当一部分用户请求,从而大大减轻了 GSLB 处理压力,使得系统抗流量冲击能力显著提升,这也是很多商业 CDN 选择 DNS 机制做全局负载均衡的原因之一。但弊端在于,如果在 DNS 缓存刷新间隔之内系统发生影响用户服务的变化,比如某个节点故障,某个链路拥塞等,用户依然会被调度到故障点去。

智能 DNS 实现浅析

基于 DNS 解析方式的 GSLB 的实现关键,就在于使 DNS “智能化”。简单来说,就是通过建立 IP 地址访问列表,判断用户的访问来源,以确定其访问节点的位置。下面浅析如何实现智能 DNS:

IP 地址收集策略

由于基于 DNS 解析方式的 CDN 使用 LDNS 进行寻址,因此我们只需要收集互联网上 DNS 服务器的 IP 地址。这样一来,收集的数量就会大大降低。为了更进一步缩小范围,一般使用 IP 地址加子网掩码的形式,如 123.175.0.0/16。在 IP 地址列表文件,就这么一行,却可以囊括很多 DNS 服务器。

IP 地址收集方法

除了可以跟第三方购买 IP 地址段之外,这里重点介绍下如何自行收集 IP 地址段。

ICANN

ICANN —— 一个负责 IP 地址分配以及域名管理的机构,与之关联的五个 RIR 机构负责替 ICANN 分配与登记部分区域的 IP 地址段:

RIR Region
AFRINIC Africa region
APNIC Asia and Pacific region
ARIN Canada, many Caribbean and North Atlantic islands, and the United States
LACNIC Latin America and parts of the Caribbean
RIPE NCC Europe, the Middle East and parts of Central Asia

可见,亚太地区的 IP 地址由 APNIC 分配,访问这里可以知道在何处得到 IP 地址分配的有用信息。进入 FTP ,阅读 README 以了解该下载哪个文件以及文件的格式。下载 delegated-apnic-latest 文件,过滤出分配给中国大陆(CN)的 IP 地址。

然后可以通过 ICANN LookupCNNIC IP 地址注册信息查询系统查询这个地址段属于哪个运营商,但一次只能查询一个地址段,根本无法手工完成所有地址段的查询,因此推荐在 Linux 下使用 whois 命令以遍历的方式逐个查询,然后按关键字归类、去重、排序,按运营商产生几个独立的文件。如果各 IP 地址租用方未能按统一的标准在 APNIC 提交注册信息则需要特殊处理。

IP 地址列表使用

最后,将每个 IP 地址列表文件关联一个 Bind 的视图 View。定义视图的目的在于,当有来自某个文件所列 IP 范围内的客户发起查询请求时,使用本视图的区文件进行域名解析。通俗的说,就是让某个运营商线路的用户,去访问某个运营商机房的服务器。

使用 Vim 也有好几年了,虽然这款“编辑器之神”的学习曲线非常陡峭,但一旦上手将会极大提高文本编辑效率,因此值得投入精力学习。

Vim

此外,Vim 哲学早已走出编辑器范畴,渗透到各种工具,例如:

Vim

本文我将会从三个方面总结 Vim 的知识。

四种常用模式

Vim 效率之高的秘密,就在于它拥有多种“模式”。如果你已经习惯了 Windows 下的编辑器,这些模式在一开始会很违反你的使用直觉。因此学习 Vim 的第一件事,就是要习惯这些模式之间的切换。

Vim 共具有 6 种基本模式和 5 种派生模式,下面只介绍最常用的 4 个基本模式:

普通模式(NORMAL MODE)

Vim 启动后的默认模式。这正好和许多新用户期待的操作方式相反,因为大多数编辑器的默认模式为插入模式(就是一打开编辑器就可以开始码字)。

Vim 强大的编辑能力中很大部分是来自于其普通模式的命令(及组合)。在普通模式下,用户可以执行一般的编辑器命令,比如移动光标,删除文本等等。如果进一步学习各种各样的文本间移动/跳转命令和其它编辑命令,并且能够灵活组合使用的话,能够比那些没有模式的编辑器更加高效的进行文本编辑。

下面介绍普通模式下几类常用的快捷键:

移动命令

跨行移动:

快捷键 说明
hjkl VIM allows using the cursor keys in order to move around. However, for a pure VIM experience you should stick to using ‘h’, ‘j’, ‘k’ and ‘l’. It’s considered more efficient since you don’t have to move your hand from the home row when you’re typing.
gg 到第一行
G 到最后一行
nG 到第 n 行
% 匹配括号移动,包括 () {} [](需要先把光标先移到括号上)
* 匹配光标当前所在的单词(# 反向)

当前行移动:

快捷键 说明
0 到行头($ 反向)
^ 到本行第一个非 blank 字符的位置(所谓 blank 字符就是空格、tab、换行、回车等)
w 到下一个单词的开头(b 反向)
e 到下一个单词的结尾
f Find next character(F 反向)
fi 到字符 i 处
4fi 到第四个字符 i 处
t Find before character(T 反向)

Vim 当前行移动

编辑命令

文本替换:

快捷键 说明
r Replace current character
When you need to replace only one character under your cursor, without changing to insert mode, use r.

剪切/复制/粘贴:

快捷键 说明
x Cut current character
d dd Cut current line
dt Cut till …
y yy Copy current line (yank)
yt Copy till …
p Paste

缩进/补全:

快捷键 说明
<< 左缩进
>> 右缩进
= 自动缩进
Ctrl + p 在 Insert 模式下,自动补全…

从别的编辑器里粘贴到 vim 里的代码经常由于不正常的缩进变得格式混乱,可以使用如下命令:

  • 自动缩进当前行: ==

  • 全文格式化:gg=G ,即:

    1. gg - Goto the beginning of the file
    2. = - apply indentation
    3. G - till end of file

重复命令

快捷键 说明
. 重复执行上一个命令
n<command> 重复执行某个命令 n 次
<start position><command><end position> 对某段起止文本执行某个命令,例如:d(删除)、y(复制)、v(选择)、gU(变大写)、gu(变小写)。例如:gg=G

插入模式(INSERT MODE)

在这个模式中,大多数按键都会向文本缓冲中插入文本。大多数新用户希望文本编辑器在编辑过程中一直保持这个模式。

使用以下快捷键进入插入模式:

当前行插入

快捷键 说明
i Switch to insert mode on current character
a Switch to insert mode after current character
I Switch to insert mode on first visible character of the current line
A Switch to insert mode on last visible character of the current line

另起一行插入

快捷键 说明
o Switch to insert mode after current line
O Switch to insert mode before current line

可视模式(VISUAL MODE)

这个模式与普通模式比较相似。但是移动命令会扩大高亮的文本区域。高亮区域可以是字符、行或者是一块文本。当执行一个非移动命令(例如复制、删除)时,命令会被执行到这块高亮的区域上。

使用以下快捷键进入可视模式:

快捷键 说明
Ctrl + v Switch to visual block mode
v Switch to visual character mode
V Switch to visual line mode

命令行模式(COMMAND MODE)

在命令行模式中可以输入命令。在命令执行完后,Vim 返回到命令行模式之前的模式,通常是普通模式。

使用以下快捷键进入命令行模式:

快捷键 说明
: 执行命令:
:h 帮助文档,例如查看 s文本替换命令(substitude)的帮助::h s
! 过滤命令
/? 搜索字符串

[range] 有以下一些表示方法,例如常用的 % 表示替换所有行,等价于 1,$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
不写 range   :  默认为当前光标所在行。
. : 当前光标所在行。
$ : 最后一行。
1 : 第1行。
33 : 第33行。
'a : 标记a所在的行(之前要使用ma做过标记)。

通过 +、- 设置相对偏移量:
.+1 : 当前光标所在行的下一行。
$-1 : 倒数第二行。(这里说明我们可以对某一行加减某个数值来取得相对的行)。

通过 , 设置范围:
22,33 : 第22~33行。
1,$ : 第1行 到 最后一行。
1,. : 第1行 到 当前行。
.,$ : 当前行 到 最后一行。
'a,'b : 标记a所在的行 到 标记b所在的行。

% : 所有行(与 1,$ 等价)。

?chapter? : 从当前位置向上搜索,找到的第一个chapter所在的行。(其中chapter可以是任何字符串或者正则表达式。
/chapter/ : 从当前位置向下搜索,找到的第一个chapter所在的行。(其中chapter可以是任何字符串或者正则表达式。

[flags] 有以下一些表示方法:

1
2
3
4
5
6
7
8
无      :  只对指定范围内的第一个匹配项进行替换。
g : 对指定范围内的所有匹配项进行替换。
c : 在替换前请求用户确认。
e : 忽略执行过程中的错误。
i : Ignore case for the pattern.
I : Don't ignore case for the pattern.

注意:上面的所有flags都可以组合起来使用,比如 gc 表示对指定范围内的所有匹配项进行替换,并且在每一次替换之前都会请用户确认。

Substitute Text

命令 描述
:[range]s[ubstitute]/{pattern}/{string}/[flags] [count] For each line in [range] replace a match of {pattern} with {string}.

详细命令:

1
2
3
4
5
6
7
8
9
10
11
4.2 Substitute                                          *:substitute*
*:s* *:su*
:[range]s[ubstitute]/{pattern}/{string}/[flags] [count]
For each line in [range] replace a match of {pattern} with {string}.
For the {pattern} see |pattern|.
{string} can be a literal string, or something special; see |sub-replace-special|.
When [range] and [count] are omitted, replace in the current line only.
When [count] is given, replace in [count] lines, starting with the last line in [range].
When [range] is omitted start in the current line.
Also see |cmdline-ranges|.
See |:s_flags| for [flags].

例子,批量替换所有空格:

  • : 进入命令行模式
  • % 表示所有行(与 1,$ 等价)
  • s 表示文本替换命令
  • \s 表示空格
  • g 表示对指定范围内的所有匹配项进行替换
1
:%s/\s//g

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
1.  替换当前行中的内容:    :s/from/to/
:s/from/to/ : 将当前行中的第一个from,替换成to。如果当前行含有多个from,则只会替换其中的第一个。
:s/from/to/g : 将当前行中的所有from都替换成to。
:s/from/to/gc : 将当前行中的所有from都替换成to,但是每一次替换之前都会询问请求用户确认此操作。

注意:这里的from和to都可以是任何字符串,其中from还可以是正则表达式。

2. 替换某一行的内容: :33s/from/to/g
:.s/from/to/g : 在当前行进行替换操作。
:33s/from/to/g : 在第33行进行替换操作。
:$s/from/to/g : 在最后一行进行替换操作。

3. 替换某些行的内容: :10,20s/from/to/g
:10,20s/from/to/g : 对第10行到第20行的内容进行替换。
:1,$s/from/to/g : 对第一行到最后一行的内容进行替换(即全部文本)。
:1,.s/from/to/g : 对第一行到当前行的内容进行替换。
:.,$s/from/to/g : 对当前行到最后一行的内容进行替换。
:'a,'bs/from/to/g : 对标记a和b之间的行(含a和b所在的行)进行替换。其中a和b是之前用m命令所做的标记。

4. 替换所有行的内容: :%s/from/to/g
:%s/from/to/g : 对所有行的内容进行替换。

Deleting text

命令 描述
:[range]d[elete] [x] Delete [range] lines (default: current line) [into register x].
:[range]d[elete] [x] {count} Delete {count} lines, starting with [range](default: current line) [into register x].
:[range]j[oin][!] [flags] Join [range] lines.
:[range]j[oin][!] {count} [flags] Join {count} lines, starting with [range] (default: current line).

例子:

1
2
# 刪除 1-10 行
:1,10d

Copying and moving text

命令 描述
:[range]y[ank] [x] Yank [range] lines [into register x].
:[range]y[ank] [x] {count} Yank {count} lines, starting with last line number in [range] (default: current line), [into register x].
:[range]co[py] {address} Copy the lines given by [range] to below the line given by {address}.
:[range]m[ove] {address} Move the lines given by [range] to below the line given by {address}.

Formatting text

Shifting lines left or right:

命令 描述
:[range]< Shift [range] lines one 'shiftwidth' left. Repeat '<' for shifting multiple 'shiftwidth's.
:[range]< {count} Shift {count} lines one 'shiftwidth' left, starting with [range] (default current line). Repeat ‘<’ for shifting multiple 'shiftwidth's.
:[range]> [flags] Shift {count} [range] lines one 'shiftwidth' right. Repeat '>' for shifting multiple 'shiftwidth's.
:[range]> {count} [flags] Shift {count} lines one 'shiftwidth' right, starting with [range] (default current line). Repeat '>' for shifting multiple 'shiftwidth's.

Left-align, right-align lines or center lines:

命令 描述
:[range]le[ft] [indent] Left-align lines in [range]. Sets the indent in the lines to [indent] (default 0).
:[range]ri[ght] [width] Right-align lines in [range] at [width] columns
(default 'textwidth' or 80 when 'textwidth' is 0).
:[range]ce[nter] [width] Center lines in [range] between [width] columns
(default 'textwidth' or 80 when 'textwidth' is 0).

Sorting text

命令 描述
:[range]sor[t][!] [b][f][i][n][o][r][u][x] [/{pattern}/] Sort lines in [range]. When no range is given all lines are sorted.
With [!] the order is reversed.
With [i] case is ignored.

配置

使用 Vim 年月较久后总会定制一套个性化的 Vim 配置,例如截取一段常用的 ~/.vimrc 配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
set number                  " 显示行号
set cursorline " 突出显示当前行
set ruler " 打开状态栏标尺
set shiftwidth=4 " 设定 << 和 >> 命令缩进时的宽度为 4
set softtabstop=4 " 使得按退格键时可以一次删掉 4 个空格
set tabstop=4 " 设定 tab 长度为 4
set nowrapscan " 禁止在搜索到文件两端时重新搜索
set incsearch " 输入搜索内容时就显示搜索结果
set hlsearch " 高亮显示搜索结果
syntax on " 程序语法开关
inoremap jj <ESC> " 重映射 ESCAPE 键
" 定义缩写:ab [缩写] [要替换的文字]
ab asap as soon as possible

另外注意,Vim 的操作记录会写入 ~/.viminfo

JSON 格式化

1
2
3
4
5
6
# JSON 格式化
# : 进入命令行模式
# % [range] 参数,指定所有行
# ! 执行具体的命令,这里使用 python
# -m json.tool 调用python里json.tool这个模块
:%!python -m json.tool

可以将该常用命令放到 ~/.vimrc 配置文件中,方便使用:

1
2
3
4
5
6
7
8
9
10
11
12
" F3 快捷键 JSON 格式化当前行
map <F3> :.!python -m json.tool<CR>
" F4 快捷键 JSON 格式化全文
map <F4> :%!python -m json.tool<CR>

" :JsonFormat 命令格式化全文
command! JsonFormat :execute '%!python -m json.tool'
" :JsonFormat 命令格式化全文,并解决汉字以 unicode 码显示问题,参考:http://qiita.com/tomoemon/items/cc29b414a63e08cd4f89
command! JsonFormat :execute '%!python -m json.tool'
\ | :execute '%!python -c "import re,sys;chr=__builtins__.__dict__.get(\"unichr\", chr);sys.stdout.write(re.sub(r\"\\u[0-9a-f]{4}\", lambda x: chr(int(\"0x\" + x.group(0)[2:], 16)).encode(\"utf-8\"), sys.stdin.read()))"'
\ | :set ft=javascript
\ | :1

设置键盘映射

https://blog.csdn.net/lym152898/article/details/52171494

各种版本

GVim

GVim 是 Windows 版的 Vim,因为有了标准的 Windows 风格的图形界面,所以叫 G(Graphical)Vim。

GVim 的多标签切换:

快捷键 说明
:tabnew 新建标签页
:tabs 显示已打开标签页的列表
:tabc 关闭当前标签页
:tabn 移动到下一个标签页
:tabp 移动到上一个标签页
:tabfirst 移动到第一个标签页
:tablast 移动到最后一个标签页

字符集配置参考 这里,其它小技巧参考 这里

MacVim

https://github.com/macvim-dev/macvim

其它

Editing a .jar with vim

一般来说,jar 包可以通用 vim 直接编辑。但要注意的是,Spring Boot 插件打包的可执行的 jar 无法使用 vim 浏览并编辑内部文件。

参考

https://en.wikipedia.org/wiki/Vim_(text_editor)

https://zh.wikipedia.org/wiki/Vim

https://en.wikipedia.org/wiki/Editor_war

https://zh.wikipedia.org/zh-hk/编辑器之战

https://missing-semester-cn.github.io/

为什么 Vim 使用 HJKL 键作为方向键

简明 Vim 练级攻略

Vim文本替换命令

12 个关于 vim 的编辑技巧

Vim 分屏基本操作详解

Vim 最全图解

VIM 插件:https://vimawesome.com/plugin/json-vim

Vim philosophy