在分布式系统存在多个 Shard 的场景中, 同时在各个 Shard 插入数据时, 怎么给这些数据生成全局的 unique ID? 在单机系统中 (例如一个 MySQL 实例), unique ID 的生成是非常简单的, 直接利用 MySQL 自带的自增 ID 功能就可以实现.
但在一个存在多个 Shards 的分布式系统 (例如多个 MySQL 实例组成一个集群, 在这个集群中插入数据), 这个问题会变得复杂, 所生成的全局的 unique ID 要满足以下需求:
在要满足前面 6 点要求的场景中, 怎么来生成全局 unique ID 呢?
数据库单表,使用 auto increment
来生成唯一全局递增ID。
优势是无需额外附加操作,定长增长,单表结构中唯一性,劣势是高并发下性能不佳,生产的上限是数据库服务器单机的上限,水平扩展困难,分布式数据库下,无法保证唯一性。
如果没有上面这些限制, 问题会相对简单, 例如: 直接利用 UUID.randomUUID() 接口来生成 unique ID (http://www.ietf.org/rfc/rfc4122.txt). 但这个方案生成的 ID 有 128 bits, 另外, 生成的 ID 中也没有带 Timestamp 一般编程语言中自带 UUID 实现, Java 中 UUID.randomUUID().toString()
产生的ID 不依赖数据库实现。
优势是,本地生成ID,无需远程调用,全局唯一,水平扩展能力好。劣势是,ID 有 128 bits 长,占空间大,生成字符串类型,索引效率低,生成的 ID 中没有带 Timestamp 无法保证时间递增。
Flickr 的做法1 是使用 MySQL 的自增ID, 和 replace into 语法。但他这个方案 ID 中没有带 Timestamp, 生成的 ID 不能按时间排序
创建64位自增ID,首先创建表
CREATE TABLE `Tickets64` (
`id` bigint(20) unsigned NOT NULL auto_increment,
`stub` char(1) NOT NULL default '',
PRIMARY KEY (`id`),
UNIQUE KEY `stub` (`stub`)
) ENGINE=MyISAM
SELECT * from Tickets64
假设表中有一行
+-------------------+------+
| id | stub |
+-------------------+------+
| 72157623227190423 | a |
+-------------------+------+
那么如果需要产生一个新的全局 64 bits 的ID,只要执行 SQL:
REPLACE INTO Tickets64 (stub) VALUES ('a');
SELECT LAST_INSERT_ID();
SQL 返回的ID就是要产生的全局唯一ID。使用 REPLACE INTO
代替 INSERT INTO
的好处是避免表行数太多。 stub 要设为唯一索引。
Flickr 内部运行两台 ticket servers,通过两台机器做主备和负载均衡。
TicketServer1:
auto-increment-increment = 2
auto-increment-offset = 1
TicketServer2:
auto-increment-increment = 2
auto-increment-offset = 2
Twitter 利用 Zookeeper 实现一个全局的 ID 生成服务 Snowflake: https://github.com/twitter/snowflake
Snowflake 生成的 unique ID 的组成 (由高位到低位):
一共 63 bits ,其中最高位是 0
unique ID 生成过程:
41 bits 的 Timestamp: 每次要生成一个新 ID 的时候, 都会获取一下当前的 Timestamp, 然后分两种情况生成 sequence number:
- 如果当前的 Timestamp 和前一个已生成 ID 的 Timestamp 相同 (在同一毫秒中), 就用前一个 ID 的 sequence number + 1 作为新的 sequence number (12 bits); 如果本毫秒内的所有 ID 用完, 等到下一毫秒继续 (**这个等待过程中, 不能分配出新的 ID**)
- 如果当前的 Timestamp 比前一个 ID 的 Timestamp 大, 随机生成一个初始 sequence number (12 bits) 作为本毫秒内的第一个 sequence number
10 bits 的机器号, 在 ID 分配 Worker 启动的时候, 从一个 Zookeeper 集群获取 (保证所有的 Worker 不会有重复的机器号)
整个过程中, 只有在 Worker 启动的时候会对外部有依赖 (需要从 Zookeeper 获取 Worker 号), 之后就可以独立工作了, 做到了去中心化.
异常情况讨论:
在获取当前 Timestamp 时, 如果获取到的时间戳比前一个已生成 ID 的 Timestamp 还要小怎么办? Snowflake 的做法是继续获取当前机器的时间, 直到获取到更大的 Timestamp 才能继续工作 (在这个等待过程中, 不能分配出新的 ID)
从这个异常情况可以看出, 如果 Snowflake 所运行的那些机器时钟有大的偏差时, 整个 Snowflake 系统不能正常工作 (偏差得越多, 分配新 ID 时等待的时间越久)
从 Snowflake 的官方文档 (https://github.com/twitter/snowflake/#system-clock-dependency) 中也可以看到, 它明确要求 “You should use NTP to keep your system clock accurate”. 而且最好把 NTP 配置成不会向后调整的模式. 也就是说, NTP 纠正时间时, 不会向后回拨机器时钟.
下面是 Snowflake 的其他变种, Instagram 产生 ID 的方法也借鉴 Snowflake
代码地址:https://github.com/boundary/flake
变化:
ID 长度扩展到 128 bits:
由于它用 48 bits 作为 Worker ID, 和 Mac 地址的长度一样, 这样启动时不需要和 Zookeeper 通讯获取 Worker ID. 做到了完全的去中心化
基于 Erlang ,这样做的目的是用更多的 bits 实现更小的冲突概率,这样就支持更多的 Worker 同时工作。同时, 每毫秒能分配出更多的 ID。
源代码:https://github.com/SawdustSoftware/simpleflake
Simpleflake 的思路是取消 Worker 号, 保留 41 bits 的 Timestamp, 同时把 sequence number 扩展到 22 bits;
Simpleflake 的特点:
Simpleflake 的问题就是 sequence number 完全随机生成, 会导致生成的 ID 重复的可能. 这个生成 ID 重复的概率随着每秒生成的 ID 数的增长而增长.
所以, Simpleflake 的限制就是每秒生成的 ID 不能太多 (最好小于 100次/秒, 如果大于 100次/秒的场景, Simpleflake 就不适用了, 建议切换回 Snowflake).
Instagram 参考 Flickr 的方案,结合 Twitter 的经验,利用 PostgreSQL 数据库的特性,实现了一个更加简单可靠的 ID 生成服务。 Instagram 的分布式存储方案: 把每个 Table 划分为多个逻辑分片 (logic Shard), 逻辑分片的数量可以很大, 例如 2000 个逻辑分片。然后制定一个规则, 规定每个逻辑分片被存储到哪个数据库实例上面; 数据库实例不需要很多. 例如, 对有 2 个 PostgreSQL 实例的系统 (instagram 使用 PostgreSQL); 可以使用奇数逻辑分片存放到第一个数据库实例, 偶数逻辑分片存放到第二个数据库实例的规则
每个 Table 指定一个字段作为分片字段 (例如, 对用户表, 可以指定 uid 作为分片字段)
插入一个新的数据时, 先根据分片字段的值, 决定数据被分配到哪个逻辑分片 (logic Shard) 然后再根据 logic Shard 和 PostgreSQL 实例的对应关系, 确定这条数据应该被存放到哪台 PostgreSQL 实例上
Instagram 在设计ID时考虑了如下因素:
Instagram unique ID 的组成:
假设2011年9月9号下午 5 点钟, epoch 开始于 2011 年 1 月1 号,那么已经有 1387263000 毫秒经过,那么前 41 bits 是
id = 1387263000 <<(64-41)
接下来13位由分片ID决定,假设按照 user ID 来分片,有 2000 个逻辑分片,如果用户的ID 是 31341 , 那么分片 ID 是 31341%2000 -> 1341
,所以接下来的13位是:
id |= 1341 <<(63-41-13)
最后,每个表自增来填补剩下的 bits,假设已经为表生成了 5000 个 IDs,那么下一个值是 5001,然后取模 1024
id |= (5001 % 1024)
sequence number 利用 PostgreSQL 每个 Table 上的 auto-increment sequence 来生成。如果当前表上已经有 5000 条记录, 那么这个表的下一个 auto-increment sequence 就是 5001 (直接调用 PL/PGSQL 提供的方法可以获取到) 然后把 这个 5001 对 1024 取模就得到了 10 bits 的 sequence number。
instagram 这个方案的优势在于:
利用 logic Shard 号来替换 Snowflake 使用的 Worker 号, 就不需要到中心节点获取 Worker 号了. 做到了完全去中心化
另外一个附带的好处就是, 可以通过 ID 直接知道这条记录被存放在哪个 logic Shard 上。同时, 今后做数据迁移的时候, 也是按 logic Shard 为单位做数据迁移的, 所以这种做法也不会影响到今后的数据迁移
MongoDB 的 ObjectID2 采用 12 个字节的长度,将时间戳编码在内。
http://code.flickr.net/2010/02/08/ticket-servers-distributed-unique-primary-keys-on-the-cheap/ ↩
https://docs.mongodb.com/manual/reference/method/ObjectId/#objectid ↩
Flower 是一个基于 Web 的监控和管理工具,可以用在 Celery 集群的监控和管理。和 Celery 配合使用非常不错。
Flower 可以查看 Celery 队列中 task 的数量,可以用来监控 worker 的状态并进行简单的管理,比如调整 worker 的 pool size 和 autoscale 设置,可以用来查看当前处理的 tasks, 等等。
使用 pip 安装 Flower
pip install flower
或者安装开发版
pip install https://github.com/mher/flower/zipball/master
直接使用下面命令开启本地监听 http://localhost:5555
flower -A proj --port=5555
或者从 Celery 中开启
celery flower -A proj --address=127.0.0.1 --port=5555
如果要暴露给外网访问可以 --address=0.0.0.0
:
celery flower -A worker --address=0.0.0.0 --port=5555 --basic_auth=name:password
使用 --basic_auth
来开启 HTTP 简单验证
zip 是一个非常常见的压缩工具,很多平台包括 Unix, VMS, MSDOS, OS/2, Windows 9x/NT/XP, Minix, Atari, Macintosh, Amiga, and Acorn RISC OS 等都有应用。zip 结合了打包和压缩。
参数非常多
zip options archive inpath infile
zip [-aABcdDeEfFghjklLmoqrRSTuvVwXyz!@$] [--longoption ...] [-b path] [-n suffixes] [-t date] [-tt date] [zipfile [file ...]] [-xi list]
-h
帮助-d
把压缩文件解到指定目录下-m
将文件压缩后,删除源文件-q
安静模式,压缩时不显示执行的执行过程-r
将执行的目录下所有子目录及文件一同处理-S
包含系统文件和隐藏文件-n
不覆盖已有的文件-o
覆盖已存在的文件且不要求用户确认-x
文件列表 解压缩文件,但不包括指定的 file 文件-v
查看压缩文件目录,但不解压-t
测试文件有无损坏,但不解压-j
不重建文档的目录结构,把所有文件解压到同一目录下将文件及文件夹打包到 dest.zip
中。
zip -r dest.zip path/to/folder file
zip dest.zip file1 file2
压缩时排除某些目录
zip -r dest.zip folder -x *.git*
使用密码压缩文件
zip -r -P password dest.zip file folder
解压文件
unzip dest.zip
解压文件到特定文件夹 -d
参数指定
unzip dest.zip -d /path/to/folder
不解压查看压缩包内部内容
unzip -l dest.zip
unzip -v dest.zip
在 Linux 下经常遇到 zip 乱码的情况呢,其实因为 Windows 或者其他系统下生成默认使用 GBK/GB2312 编码,而在 Linux 下默认为 UTF8,所以可以使用 unzip 的 -O
参数,这个参数 只有 unzip 1.6
及以上版本才有。
unzip -O cp936 file.zip -d /path/to/folder
Linux 下经常遇到 tar.gz tar.bz2 这样的压缩包,这个时候可能就需要使用到 tar 这个命令。
下面是一次 vim 的组内介绍的大纲。记录一下。
大纲
Normal mode
Insert mode
Visual mode
Command mode
i 进入 insert mode,在光标为之前进入插入模式
I 行首非空字符前插入 , I 等同于 `^i`
s 删除光标下字符,并进入 insert mode, 等同于 `cl`
S 删除光标所在一行,并进入 insert mode 行首 , 等同于 `^C`
a 光标之后进入 insert mode
A 光标移动到行尾并进入 insert mode , 等同于 `$a`
o 在光标下一行插入一行,并进入 insert mode , 等同于 `A<CR>`
O 在光标上一行插入新行,并进入 insert mode , O 等同于 `ko`
C 删除光标后到行尾内容,并进入 insert mode , C 其实等同于 `c$`
cc 删除行,并进入插入模式
Esc 退出 Insert 模式
Example: this is a text for testing mode.
h,j,k,l
k
^
h < > l
v
j
H,M,L
w,W,e,E,b,B
%
0,^,$
gg, G
fx,Fx,tx,Tx,
'', g; move cursor to last edit change
Example: This is a sentence for testing moving with a word and WO-RD test.
p 光标之后粘贴 (p)aste
P paste before the cursor
yy 复制当前行
y Yank 复制。Example: yw (yank word) 光标停留到词第一个字母上 yw 复制单词
y0 copy the data from cursor to begining of the line
y$ copy the data from cursor to end of the line
x 删除光标下单个字符,将其放到粘贴板,剪切
X 向前删除一个字符,相当于 Backspace
dd 删除光标所在一行,并把该行复制
dw 删除光标所在词 (d)elete (w)ord
d0 删除光标到该行最前
d^ 删除光标到行首非空白字符
d$ 删除光标到该行最后
J 删除光标所在行的换行符
r,R 替换
Operator + Motion = Action
dw delete word
da' delete what in ' and include '
Example:
This is an 'example' for "word-to-delete".
add quote to this "word"
/pattern - 正向搜索,从光标处开始向文件末搜索
?pattern - 反向搜索,从光标处开始向文件首搜索
n - 下一个,往下执行搜索命令
N - 上一个
* - Word under cursor - forward (bounded)
# - Word under cursor - backward (bounded)
:n1, n2s/p1/p2/g - 将第 n1 至 n2 行中所有 p1 均用 p2 替代
:g/p1/s//p2/g - 将文件中所有 p1 均用 p2 替换
:s/p1/p2/g - 将当前行中所有 p1 均用 p2 替代
:%s/old/new/g 全局替换
:'<,'>s/old/new/g 选中之后替换
:g/^$/d 删除所有空行
Example:
dit, di', di", di), di}, di],
yi), yi]
vi', vit
ciw, ciw)
qw 开启 record macro, 保存到寄存器 w
q 结束录制
Example:
int a = 1
int b = 2
int c = a+b
print a
print b
print c
共有 9 大类寄存器
寄存器访问 “a 寄存器前加 “
其他操作
:reg a 查看寄存器
"" noname buffer last dcsxy
"_ blackhole register
"% filename register
"/ last search register
": last command
insert mode <C-r> a insert text in register a
"ap Normal mode paste text in register a
surround.vim
cs"' change surround " to '
cs'<q> change surround ' to <q>
ds' delete surround '
ysiw" add " to word
yss) add ) to entire line
Example:
"Hello world!" and other strings
vimutor
更快的编码效率
Charles 是Mac上一款非常流行的抓包软件 , 但其实各大平台上 Charles 都有安装包。这篇文章主要就是讲一下在 Linux 平台上(Debian/Ubuntu) 系上安装 Charles 。
Charles 拥有 APT 源,首先安装 PGP 公钥,当前公开的秘钥为 1AD28806, 他的 fingerprint 为 4BA7 DB85 7B57 0089 7420 96E1 5F16 B97C 1AD2 8806:
wget -q -O - https://www.charlesproxy.com/packages/apt/PublicKey | sudo apt-key add -
或者
sudo apt-key adv --keyserver pgp.mit.edu --recv-keys 1AD28806
然后添加源
sudo sh -c 'echo deb https://www.charlesproxy.com/packages/apt/ charles-proxy main > /etc/apt/sources.list.d/charles.list'
最后更新源并安装
sudo apt-get update && sudo apt-get install charles-proxy
安装完成之后会在 /usr/bin
下安装可执行二进制,该源中还包含 beta 版本的 Charles,可以通过包名 charles-proxy-beta
来安装。
在官方下载最新的安装包: https://www.charlesproxy.com/download/latest-release/
charles.jar 文件在三大平台上的位置如下:
Mac: /Applications/Charles.app/Contents/Java/charles.jar
Windows: C:\Program Files\Charles\lib\charles.jar
Linux: /usr/lib/charles-proxy/charles.jar
计算机中通常所说的寄存器 Register 一般指的是 CPU 中的寄存器,用来暂存 CPU 处理所需要的指令,数据等等。 Vim 中同样也有寄存器的概念,使用的方式和概念也和 CPU 是非常类似的。
Vim 的寄存器可以看成 Vim 中额外用来存储信息的区域,虽然看不见,但是如果使用 x
, s
, y
, p
等等命令的时候都无意识的使用到了 Vim 的寄存器 (register).
Vim 中每一个 register 都可以通过添加双引号的方式来访问,比如 "a
来访问 a 寄存器。
可以通过选择然后使用 y
来将内容放到寄存器中,比如 "ay
来将选择的内容 yank 复制到 "a
寄存器。 可以使用 "ap
来粘贴 a 寄存器中的内容。
同样可以再 Insert mode 下使用 Ctrl + r 再加 a 寄存器名字来插入。这会将寄存器内容粘贴进当前编辑位置。
可以使用 :reg
命令来查看所有寄存器及其内容,或者直接在后面添加寄存器名字来查看关心的内容 :reg a b c
:reg a b c
--- Registers ---
"a register a content
"b register b content
"c register c content
Vim 有自己的 unnamed (无名寄存器)或者说默认寄存器,可以通过 ""
来访问。任何被 d
, c
, s
, x
删除或者 y
复制的内容都会被存放在该寄存器中,使用 p
粘贴时也是中该寄存器中粘贴内容。直接使用 p
其实等效于 ""p
。
Vim 提供 26 个英文字母命名的有名寄存器 (:h quote_alpha
)。这就意味着可以
"ad{motion}
"ay{motion}
"ap
or "aP
多达 26 段文本。
当使用 yank 复制一些内容,然后又执行了 d 命令,会发现粘贴时,当时 yank 的内容被 d 命令中的内容替换了,这是 Vim 非常常见的一个问题,然而问题并不在 Vim,而是 Vim 把 yank 的内容 ,delete 的内容都放到了 unnamed register 中了,但是复制的内容并没有丢失,并不需要再次回到想要复制的地方再次 yank 一遍。
这里就要介绍 Vim 的另外一种寄存器 —- 数字寄存器 numbered registers , 正如他的名字一样,数字寄存器的名字就是 "0
到 "9
。
"0
寄存器永远保存着 yank 的最新内容,其他寄存器保存这历史 9 个内容,从 1 到 9 从新到老。如果复制了新的文字,永远可以通过 "0p
来粘贴。
事实上不经 yank 的内容在 ""
中,x
, s
,c{motion}
, d{motion}
命令操作的内容都会在该寄存器中保存。
正因为所有的 yank 内容都会自动被保存在 "0
寄存器中,所以数字寄存器 "0
也被称为 yank register.
Vim 中有四个只读寄存器 ".
, "%
, ":
, "#
.
最后插入的文字被保存在 ".
中,如果想要在其他地方使用刚刚输入的文字,非常方便。
"%
保存 Vim 最早开始打开时,当前文件的完整路径。最常用的组合就是将当前的路径放到粘贴板
:let @+=@%
let
命令用来向寄存器写入, "+
是粘贴板寄存器,将 "%
内容写入粘贴板
":
是最近执行命令寄存器。如果最近保存过文件 :w
,那么 Ex 命令 w
会保存在寄存器中。可以使用 @:
来执行前一次命令。再比如使用替换命令替换了一行中的内容 :s/foo/bar
,那么将光标移动到另一行再次使用 @:
就能够再次替换。
"#
保存 alternative file 名字。大致可以理解为 Vim 中编辑的上一个文件,可以通过 :h alternate-file
来查看更多。当使用 Ctrl + ^ 来切换文件时,使用的就是这个寄存器保存的文件名。同样可以使用 :e Ctrl-r #
来做同样的事情。不常用。
"=
用来处理表达式结果,如果在 Insert Mode 使用 Ctrl+r+= ,然后输入 2+2<Enter>
,结果 4 会显示出来。也可以用来执行外部命令,比如 Ctrl - r =
之后输入 system('ls') <enter>
来显示 ls
的结果。
"/
这个寄存器保存最近搜索过的内容,包括 /
, ?
, *
, #
的内容。比如说最近搜索过 /example
,想要替换成 another
, 可以使用
:%s/<Ctrl-r/>/another/g
输入 :%s/
只有按下 Ctrl + r 再按 /
会自动插入寄存器保存的内容。
搜索寄存器也是可以写的,可以使用 let
命令写入:
:let @/="keywords"
或许很多不熟悉 Vim 寄存器的用户曾经使用过 Vim 的 Macro, Vim 可以使用 Macro 来录制一连串命令,然后重复。(可以使用 :h recording 来查看更多)。
Vim 使用寄存器来保存 Macro 的命令。比如使用 qw
来录制 Macro ,寄存器 "w
会被用来记录所有录制的内容,所有的内容都有文本的形式存放。
更加 Cool 的事情就是,因为所有的内容都以文本的形式存在寄存器中,我们可以轻易的修改其中的内容,而不至于因为一个疏忽录制错误,而重新录制整个操作。
比如忘记了给文字最后添加分号,可以
:let @W='i;'
来修改录制的内容,注意 W
是大写,这是追加到寄存器的意思,插入 i;
进入插入模式并输入 ;
。
再比如如果需要直接修改 register 的内容,可以使用
:let @w='<Ctrl-r w>
然后修改需要修改的内容,然后以 '
结束。
另外一个 Cool 的事情就是,因为所有的命令都是以文本的形式保存的,所以可以轻易的将录制的 macro 移动到另外的寄存器中,或者将录制的 macro 分享给其他人。
比如将录制在 w
寄存器中的内容复制给粘贴板寄存器,然后就能在其他 Vim 中使用。
黑洞寄存器 "_
放入该寄存器的内容都会被吞噬掉,不会有任何返回。所以不想污染 yank register 的时候可以手动的将内容导入到该寄存器中,比如数字 0 寄存器已经有了想要粘贴的内容,这时想要删除一些内容,粘贴数字 0 寄存器中的内容,那么就可以:
"_diw
删除该单词,并将内容放到 black hole register 中,再使用
P
来粘贴即可。
系统粘贴板寄存器 "+
当 Vim 要和外部程序或者系统交互时用那些 Vim 自身的寄存器就不管用了,Vim 的 plus register 指向的是系统粘贴板,用 "+
来表示。
如果想要在 Vim 中粘贴系统剪贴板内容,那么需要使用 "+p
(或者在插入模式下 <C-r>+
).
反之如果要 yank 或者 delete 内容到粘贴板需要使用 "+
在 X11 系统中还有一个粘贴板,被称为 primary, 所以 Vim 对应一类寄存器 "*
,一般被鼠标中间按键使用。
在 Windows 和 Mac OS X 中没有 primary clipboard 所以 "+
和 "*
可以混用。
Vim 可以在编译时选择是否支持 X11 粘贴板,可以使用 :version
来查看 xterm_clipboard
查看是否支持。如果前缀是一个 -
减号,那么表示该版本的 Vim 不支持该特性。+
加号表示支持特性。
看到上面这么多寄存器,不免就要思考,不如想要把当前单词复制到指定的寄存器中该怎么办。其实非常简单,在命令前加上寄存器的名字,比如要将单词保存到寄存器 "a
"ayiw
或者复制该行到寄存器 b
"byy
如果要将寄存器内容粘贴到文本,可以
"ap
除了上面普通模式的命令,Vim 也有删除、复制与粘贴的 Ex 命令,比如将当前行剪切到寄存器 c
:delete c
将寄存器内容粘贴到当前光标所在行
:put c
Vim 中的寄存器一共分为 9 大类:
其中大部分的寄存器上文都有涉及,其中为涉及到的 selection and drop registers 和 GUI 中选取的内容有关系。
black hole register 是一个黑洞寄存器,当写入 black hole register 时,nothing happens . 像黑洞一样吞掉所有的输入,可以用来在大量删除文本时不影响任何寄存器。
normal 命令指定是 Vim 下可以通过 normal 来指定执行 Normal 模式下的命令,以便于达到快速编辑大量文本的操作。
比如说要批量注释一批代码,需要在行前插入 #
替换:
:%s/^/#/g
visual block:
gg<Ctrl-v>I#<Esc>
注释第一行后用 dot command 重复执行每一行
.
我们可以用 normal 命令实现上述需求,步骤:
光标定位到首行,执行:
I#<Esc>
选中之后的所有行
jVG
然后执行
:'<,'>normal .
这样刚刚选中的行都将执行 .
代表的最后一次操作。注:只要输入 :
就能实现:'<,'>
,你可以注意 Vim 的左下角的提示。
当然如果不适用 Visual block 那也可以指定具体的操作对象,比如::%normal I#
,%
代表这个文件,代表着注释整个文件,当然也可以选择具体的范围,如::1,4normal I#
表示注释 1 到 4 行。
总结::normal 命令可以执行任何 normal 模式下的命令,更多帮助::help normal
。
Vim 的设计哲学中有这样一句话:”if you write a thing once, it is okay. However if you’re writing it twice or more times, then you should find a better way to do it”.
Vim 的 Macro 就是用来解决重复的问题。在 Vim 寄存器的文章里面已经对 macro 有所涉及, macro 的操作都是以文本的方式存放在寄存器中。
录制 macro,在普通模式下使用 q + [a-z] 26 个字母中的一个:
q[a-z]
之后的命令都会被记录,然后结束时再按一下 q
。
执行 macro 的时候,在寄存器前加 @
,比如记录在寄存器 a 中,要执行 10 次的话:
10@a
执行 10 遍 a 寄存器中记录的命令。
使用 @@
快速再执行一遍上一次的命令。
比如说想要给文件每一行编号,可以使用简单的 vim script.
在普通模式下
然后对选中的文本
:'<,'>normal @a
对选中文本执行 a假设已经有一个 macro 保存在了 a 中,可以使用
'
结束 Enter
退出macro 内容保存在 a
中,直接使用 :reg a
来查看内容即可。
注释,或者在每一行的末尾添加特定字符,比如在每行末加上分号”;” ,对于这个操作 Vim 中有太多的方式可以完成,比如说替换 :%s/$/;/g
,比如说 .
命令,再比如这里要使用的 macro :
int a = 1
int b = 2
int c = a+b
print a
print b
print c
如果是用 .
来实现的话,首先在第一行执行A;
,然后重复 5 次执行j.
,对于这种简单文件来说很容易使用,但是如果这个文件有 1000 行,那么显然 .
命令是不可行的。使用 macro , 可以先录制一遍,然后在 1000 行上执行便可。
比如可以在 normal 模式下使用 qaA;<Esc>jq
qa
开启录制,存入 a 寄存器<Esc>
退出插入模式此时 a 寄存器中就保存了当前行的操作,在当前行添加 ; 并将光标移动到下一行。
录制结束后就可以使用 @a
1000@a
执行 1000 遍 macro ,就能将下面 1000 行尾部添加 ;
可以使用 macro 实现插入 1 到 100 个数字,每一行自增一:
1
2
3
...
100
首先在第一行插入 1,然后光标定位了“1”处,进入 normal 模式
输入一下命令
qayyp<Ctrl>aq
<Ctrl>a
数字 +1最后执行
98@a
很早开始我就使用 haroopad ,同样是一款简洁,跨平台的 markdown 编辑器,而在 Linux 平台除非遇到特殊情况想要仔细预览效果,我也一般都只在 Vim 中直接以纯文本形式编辑 markdown。但是为什么要再来试用这样一个编辑器呢,一是我不止一次得看到别人推荐,二来也是好奇心作祟。
Typora 一如她官网 —- https://typora.io/ 的简洁。没有多余的元素,直接面向文本,好评。下面也不用介绍他的功能多少多少,这种一般直接看他的官方 feature 介绍,看使用帮助就行。下面就介绍几个比较让我开心的功能。
在文本编辑器左侧边可以打开目录下所有文件,这一点是 haroopad 所没有的,如果编辑文本需要打开多个文件,那么使用之前的 haroopad 就需要同时打开多个文件,而在 Typora 中则直接在边上选择要切换的文件即可。在 Vim 中其实也用了 Nerd Tree 来实现这样的功能,能够很方便的在文件之间切换。
Typora 有一个独立的 Outline 面板,可以轻松的在长篇文章中进行导航,快速定位。
haroopad 能够导出的格式只有简单的 HTML 和 PDF,而 Typora 提供了更多的选择,甚至连 Word 格式也都支持,支持的格式包括 pdf, html, docx, OpenOffice, rtf, Epub, LaTex, Media Wiki, reStructuredText, Textile, OPML 等等
不如 haroopad 一样提供的 Vim 键支持,其实如果对 Markdown 语法熟悉,也不在乎所见即所得的UI显示,完全可以使用 Vim 作为唯一的 MD 编辑,没有那么多漂亮的界面,和快速的操作,只要 Vim 都可以搞定。
这里有一篇 Typora 作者创造 Typora 的一些想法 ,其中提到了取消了独立的预览 Panel,让所有的内容所见即所得,这也是
Windows 上有 ipconfig ,Linux 上有一个类似的工具,就是 ifconfig (interfaces config)。ifconfig 工具不仅可以被用来简单地获取网络接口配置信息,还可以修改这些配置。
慎重提示:下面的所有相关命令,在清除明白其含义之后再输入到终端,另外不要远程到服务器执行,下面的命令可能导致远程服务器网络配置异常
ifconfig [device] [parameter]
命令功能:
ifconfig 命令用来查看和配置网络设备。当网络环境发生改变时可通过此命令对网络进行相应的配置。
命令参数:
-promisc
设置是否支持网卡的 promiscuous 模式,如果选择此参数,网卡将接收网络中发给它所有的数据包-a
显示全部接口信息命令
ifconfig
输出
eth0 Link encap:Ethernet HWaddr f2:3c:91:2d:a3:0a
inet addr:192.169.31.204 Bcast:192.168.31.255 Mask:255.255.255.0
inet6 addr: fea0::f13c:91ff:fe2d:a30a/64 Scope:Link
inet6 addr: 2400:8903::f33c:92ff:ff2d:a31a/64 Scope:Global
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:18740952 errors:0 dropped:0 overruns:0 frame:0
TX packets:17044866 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:17987424394 (17.9 GB) TX bytes:18563777089 (18.5 GB)
说明
eth0 表示第一块网卡,如果有其他网卡可能会有 eth1, eth2 等等
lo 是表示主机的回环地址,一般用来测试本地网络程序,只能在此台主机上运行和查看的网络接口。比如把 Nginx 服务器指定到回环地址,在浏览器输入 127.0.0.1 就能看到 Nginx 欢迎页面。
具体每一项的内容包括以下:
命令:
ifconfig eth0 up
ifconfig eth0 down
说明:
ifconfig eth0 up 为启动网卡 eth0 ;ifconfig eth0 down 为关闭网卡 eth0。
命令:
ifconfig eth0 add 33ffe:3240:800:1005::2/64
ifconfig eth0 del 33ffe:3240:800:1005::2/64
说明:
ifconfig eth0 add 33ffe:3240:800:1005::2/64 为网卡 eth0 配置 IPv6 地址;
ifconfig eth0 add 33ffe:3240:800:1005::2/64 为网卡 eth0 删除 IPv6 地址;
命令:
ifconfig eth0 hw ether 00:AA:BB:CC:DD:EE
命令:
ifconfig eth0 192.168.120.56
ifconfig eth0 192.168.120.56 netmask 255.255.255.0
ifconfig eth0 192.168.120.56 netmask 255.255.255.0 broadcast 192.168.120.255
说明:
上面几行的命令解释如下:
ARP 协议就是一个根据 IP 地址获取物理地址的一个协议。
命令:
ifconfig eth0 arp
ifconfig eth0 -arp
说明:
ifconfig eth0 arp
开启网卡 eth0 的 arp 协议;
ifconfig eth0 -arp
关闭网卡 eth0 的 arp 协议;
命令:
ifconfig eth0 mtu 1500
说明:
设置能通过的最大数据包大小为 1500 bytes
备注:用 ifconfig 命令配置的网卡信息,在网卡重启后机器重启后,配置就不存在。要想将上述的配置信息永远的存的电脑里,那就要修改网卡的配置文件了。