在使用交互式 Bash 时,一个配置得当的命令提示符可以为用户带来不少便利,本文讲解如何配置命令提示符。

默认提示符

命令提示符涉及到以下两个环境变量:

环境变量 描述
PS1 主提示符,Bash 会在准备好读入一条命令时显示,默认值 \s-\v\$
PS2 次提示符,Bash 会在需要更多的输入来完成一条命令时显示,默认值 >

定制提示符

Bash 允许通过插入一些反斜杠转义的特殊字符来定制这些提示符,常用的转义字符如下:

转义字符 描述
\h 主机名,第一个 . 之前的部分
\H 主机名
\j shell 当前管理的作业数量
\l shell 的终端设备名的基本部分
\n 新行符
\r 回车
\s shell 的名称, $0 的基本部分 (最后一个斜杠后面的部分)
\u 当前用户的用户名
\v bash 的版本 (例如,4.3)
\w 当前工作目录
\W 当前工作目录的基本部分
\! 此命令的历史编号
\# 此命令的命令编号
\$ 如果有效 UID 为 0,则显示 #, 否则 $
一个反斜杠

除此之外,还有一些不太常用的日期转义字符:

转义字符 描述
\d 当前日期,格式是 “星期 月份 日” (例如,”Tue May 26”)
\D{format} 自定义日期格式,花括号是必需的
\t 当前时间,采用 24 小时制的 HH:MM:SS 格式
\T 当前时间,采用 12 小时制的 HH:MM:SS 格式
\@ 当前时间,采用 12 小时制上午/下午 am/pm 格式
\A 当前时间,采用 24 小时制上午/下午格式

如何定制

由于 PS1 默认设置的 \s-\v\$ 实在是太废毫无信息量可言,显示如下:

1
bash-4.3$

因此可以通过修改 ~/.bash_profile 文件来定制自己的命令提示符。例如,使用 CentOS 默认设置的 [\u@\h \W]\$

1
$ vim ~/.bash_profile

export PS1="[\u@\h \w]\$ "

定制后,能够知道当前用户、主机名、工作目录:

1
[root@BGP-BJ-C-5HL ~]$

从 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 命令使用正则表达式以行为单位进行文本搜索(global search regular expression(RE) and print out the line),其命令格式如下:

1
grep [选项] 'PATTERN' [文本文件]
选项 描述
-c, --count 打印匹配的行数
-n, --line-number 打印行号
-v, --revert-match 反转查找
-A , --after 可加数字,表示打印后面n行
-B , --before 可加数字,表示打印前面n行

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 命令以行为单位,用于对文本文件内容进行排序。其命令格式如下:

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

常用的选项如下:

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

uniq

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

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

常用的选项如下:

选项 描述
-i 忽略大小写
-c 进行计数
-d 只显示重复行
-u 只显示不重复的行

wc

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

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

常用的选项如下:

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

tee

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

tee

例子

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
$ cat /data/tradehistory_2015080*.txt | cut -d ',' -f 13 | sort | uniq -c | sort -nr
29549 20150803
24086 20150805
19520 20150804

简单命令(Simple Commands)

管道(Pipelines)

pipeline(管道)是一个或多个命令的序列,用字符 | 分隔。管道的命令格式如下:

1
command [ | command2 ... ]

管道的特点如下:

  • 管道是一个由“标准输入输出”链接起来的进程集合;
  • 管道中的每个命令都作为单独的进程来执行(即在一个子 shell 中启动);
  • 每一个进程的输出(stdout)被直接作为下一个进程的输入(stdin);
  • 管道命令不处理 standard error output(stderr);
  • 管道的符号为:|

管道的处理流程如下图:

pipe

重定向(Redirection)

在命令执行前,它的输入和输出可能被 redirected (重定向),该功能可以用于如下场景:

  • 屏幕输出的信息很重要,而且需要将它存下来时;
  • 一些运行命令的可能已知错误信息,想以 2> /dev/null 将它丢掉;
  • 一些系统的例行命令(例如写在 /etc/crontab)的运行结果,需要存下来时;
  • 错误信息与正确信息需要分别输出时。

例子:

快速创建带内容的文件

1
2
$ echo "hello world" > /tmp/file
$ cat /tmp/file

将已知错误信息丢弃

1
$ find /tmp/ -name 2> /dev/null

描述符(Descriptor Number)

描述符 描述
0 标准输入(stdin)
1 标准输出(stdout)
2 标准错误输出(stderr)

操作符(Operator)

操作符 描述
< 重定向输入(Redirecting Input)
> 重定向输出(Redirecting Output),与 1> 等价
>> 追加到重定向输出(Appending Redirected Output)
2> 重定向错误输出(Redirecting Error)
2>> 追加到重定向错误输出(Appending Redirected Error)
&> 重定向标准输出和标准错误输出(Redirecting Standard Output and Standard Error)。 推荐使用,它与 >word 2>&1 在语义上等价
>& 同上,但不推荐使用
2>&1 将标准错误输出重定向到标准输出

序列(Lists)

list(序列)是一个或多个管道,用操作符 ;, &, &&, 或 || 分隔的序列, 并且可以选择用 ;, &, 或 \新行符结束。

操作符 例子 描述
&& command1 && command2 一个 AND 序列。command2 只有在 command1 返回 0 时才被执行
\ \ command1 \ \ command2 一个 OR 序列。command2 只有在 command1 返回非 0 状态时才被执行
; command1; command2 结束一个序列。不考虑命令的退出状态,连续执行命令
\ command\ 结束一个序列
& command1 & 如果一个命令是由 & 结束的, shell 将在后台的子 shell 中执行这个命令
  • AND 和 OR 序列的返回状态是序列中最后执行的命令的返回状态。
  • 这些序列操作符中, && 和 || 优先级相同, ; 和 & 优先级相同。

退出状态(Exit Status)

  • 从 shell 的角度看,一个命令退出状态是 0 意味着成功退出。 非零状态值表明失败。
  • 如果没有找到命令,为执行它而创建的子进程返回 127。
  • 如果找到了命令但是文件不可执行,返回状态是 126。
  • 如果命令由于扩展或重定向错误而失败,退出状态大于零。

使用场景

在某些情况下,很多命令我想要一次输入去运行,有两种方法:

  1. Shell Script
  2. 使用序列

例如,一串无人值守源代码形式安装的命令如下:

1
$ ./configure && make && make install

复合命令(Compound Commands)

compound command(复合命令)是如下情况之一:

  • (list)
  • { list; }
  • ((expression))
  • [[ expression ]]

  • if list; then list; [ elif list; then list; ] ... [ else list; ] fi

  • case word in [ [(] pattern [ | pattern ]

  • while list; do list; done

  • until list; do list; done
  • for name [ in word ] ; do list ; done
  • for (( expr1 ; expr2 ; expr3 )) ; do list ; done

  • select name [ in word ] ; do list ; done

算术求值(Arithmetic Evaluation)

条件表达式(Conditional Expressions)

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 仅列出目录本身,而不是列出目录内的文件。
-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
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

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 默认值,不列出空行行号。
base64 base64 encode/decode data and print to standard output

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 来终止。
split Split a file into pieces 用于按行、按大小分割文件
csplit Split a file into context-determined pieces

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
cksum
md5sum
sha1sum
sha256sum
sha512sum

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 不输出末尾换行符
-e 开启转义字符,例如:反斜杠 、换行符 \n
printf Format and print data
yes Print a string until interrupted 使用管道自动输入“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 截取出目录名

参考

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 地址。

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

IP 地址列表使用

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

工作、生活中常与域名打交道,尤其是近期需要为公司的系统上 CDN 服务,因此对 CDN 的核心 DNS 做了一番重温、总结。

DNS 是什么?

DNS 是互联网的一项基础服务,它将人类易记的域名解析为不易记的 IP 地址,使人更方便的访问互联网。

DNS 的结构?

域名系统(DNS)是一个多层级、分布式的系统,就如同一个树状结构:

1
                    +---+                                 
                    | . |   Root nameserver               
                    +-+-+                                 
                      |                                   
  +-------+-------+---+---+-------+-------+               
  |       |       |       |       |       |               
+-+-+   +-+-+   +-+-+   +-+-+   +-+-+   +-+-+    
|com|   |net|   |org|   |gov|   |cn |   |...|   Top-level domain
+---+   +---+   +-+-+   +---+   +---+   +---+             
                  |                                       
             +----+----+                     
             |wikipedia|   First-level domain                     
             +----+----+                                  
                  |                                       
          +---------------+                               
          |       |       |                               
        +-+-+   +-+-+   +-+--+                            
        |www|   |ftp|   |mail|   Resource record          
        +---+   +---+   +----+

域名系统(DNS)的每一级只知道直接下级的位置,而无法获得跨级的位置,因此在域名解析的时候,需要自上而下、逐级查询。这种机制虽然看似低效,却能够提供分布式、高容错的服务,避免让域名系统(DNS)成为一个集中式的单点系统。

DNS 如何解析域名?

对某个域名发起第一次解析请求时,负责处理递归查询的本地 DNS 服务器要发送好几次查询:

A DNS recursor consults three name servers to resolve the address www.wikipedia.org.

域名解析的时候,需要自上而下、逐级查询:先查根域,再查顶级域,再查一级域名,最终定位到 IP 地址。dig 命令加 +trace 参数可以追踪整个域名解析过程,从中了解经过的每一级 nameserver,其结果简化如下:

1
$ dig +trace www.wikipedia.org

;; global options: +cmd
.                    NS      a.root-servers.net.
.                    NS      b.root-servers.net.
.                    NS      c.root-servers.net.
.                    NS      d.root-servers.net.
.                    NS      e.root-servers.net.
.                    NS      f.root-servers.net.
.                    NS      g.root-servers.net.
.                    NS      h.root-servers.net.
.                    NS      i.root-servers.net.
.                    NS      j.root-servers.net.
.                    NS      k.root-servers.net.
.                    NS      l.root-servers.net.
.                    NS      m.root-servers.net.
;; Received 315 bytes from 202.96.128.166#53(202.96.128.166) in 642 ms

org.                 NS      a0.org.afilias-nst.info.
org.                 NS      a2.org.afilias-nst.info.
org.                 NS      b0.org.afilias-nst.org.
org.                 NS      b2.org.afilias-nst.org.
org.                 NS      c0.org.afilias-nst.info.
org.                 NS      d0.org.afilias-nst.org.
;; Received 691 bytes from 199.7.83.42#53(l.root-servers.net) in 408 ms

wikipedia.org.       NS      ns0.wikimedia.org.
wikipedia.org.       NS      ns1.wikimedia.org.
wikipedia.org.       NS      ns2.wikimedia.org.
;; Received 651 bytes from 199.19.53.1#53(c0.org.afilias-nst.info) in 1155 ms

www.wikipedia.org.   A       198.35.26.96
;; Received 90 bytes from 91.198.174.239#53(ns2.wikimedia.org) in 365 ms

下面分别介绍每一级 nameserver:

根域名(Root nameserver)

. 代表的根域名服务器(Root nameserver),是 DNS 中最高级别的域名服务器(nameserver),负责返回顶级域名的权威域名服务器(authoritative nameserver)的地址。

早期的域名必须以英文句号“.”结尾,当用户访问 www.wikipedia.org 的 HTTP 服务时必须在地址栏中输入:http://www.wikipedia.org.,这样 DNS 才能够进行域名解析。如今 DNS 服务器已经可以自动补上结尾的句号。

目前全球共有 13 组根域名服务器,以英文字母 A 到 M 依序编号,域名格式为“字母.root-servers.net”。编号相同的根域名服务器使用同一个 IP,数百台根域名服务器总共只使用 13 个 IP,因此可以抵抗针对其所进行的分布式拒绝服务攻击(DDoS)。这些根域名服务器的运行软件皆为 BINDNSD

顶级域名(Top-level domain)

顶级域名(Top-level domain, TLD)主要分为两类:

顶级域名的数量仍在不断增长中,除了英文字母的域名,还不断新增各种语系的域名,如中文域名。

一级域名(First-level domain)

组织或个人通过域名代理服务商(如 GoDaddy、万网)进行注册的域名。根据需要还可以自行在一级域名下新增二级、三级等子域名。

资源记录(Resource record)

域名系统中,一般一个域(DNS zone)通过一个 zone 文件保存该域的相关配置信息。zone 文件包含了域名和 IP 地址等资源之间的映射,以资源记录(Resource recerd, RR)的文本形式进行组织。这里列举了所有的资源记录类型。

以域名 example.com 为例,其 zone 文件简化如下:

name ttl record class record type record data comment
example.com. 1h IN NS ns ns.example.com is a nameserver for example.com
ns 1h IN A 192.0.2.2 IPv4 address for ns.example.com
example.com. 1h IN A 192.0.2.1 IPv4 address for example.com
www 1h IN CNAME example.com. www.example.com is an alias for example.com

总结

综上,注册域名、管理资源记录都是站长最常见的操作。至于域名解析就交给本地 DNS 服务器代为处理,一般用户都无需操心。

Vim

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

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

模式

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

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

普通模式

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 模式下,自动补全…

重复命令

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

插入模式

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

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

当前行插入

快捷键 说明
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

可视模式

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

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

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

命令行模式

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

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

快捷键 说明
: 执行命令:
:h 帮助文档,例如查看 f 命令的帮助::h f
:%s/vivian/sky/g 替换每一行中所有 vivian 为 sky
! 过滤命令
/? 搜索字符串

配置

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

1
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

GVim

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

GVim 的多标签切换:

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

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

参考

上文

有时候合并操作并不会如此顺利。如果在不同的分支中都修改了同一个文件的同一部分,Git 就无法干净地把两者合到一起。这种问题只能由人来裁决,解决冲突的办法无非是从冲突中二者选其一或者由你亲自整合到一起。

你完全可以手工编辑处理冲突,或者推荐使用图形化的外部合并与比较工具(mergetool)。

mergetool 是什么?

Merge tool is a GUI that steps you through each conflict, and you get to choose how to merge. Sometimes it requires a bit of hand editing afterwards, but usually it’s enough by itself. It is much better than doing the whole thing by hand certainly.

mergetool 如何选择?

mergetool 需自行选择安装。选择很多,例如:meld, opendiff, kdiff3, tkdiff, xxdiff, tortoisemerge, gvimdiff, diffuse, ecmerge, p4merge, araxis, vimdiff, emerge …

推荐使用 meld,一款优秀的可视化 diff 和代码合并工具(merge tool),支持特性如下:

  • 跨平台,支持 Linux/Unix、 OS X、Windows,多种便捷的安装方式
  • 跨工具,支持多种版本控制系统(VCS),如 Git、SVN、Mercurial …
  • 支持双方或三方文件、目录对比
  • GUI 界面好看 :)

meld 如何使用?

安装好 meld ,还需进行如下配置:

用于 git diff

首先配置好 git :

1
$ git config --global diff.external ~/meld.sh

然后准备编写 meld.sh 包装脚本:

1
$ vim ~/meld.sh

默认情况下, git diff 会传递 7 个参数给该包装脚本:

1
path old-file old-hex old-mode new-file new-hex new-mode

但我们仅仅只需要 old-filenew-file 参数,因此需要用包装脚本来传递它们。脚本内容如下:

1
2
#!/bin/sh
meld $2 $5

如果对涉及到的参数感兴趣,可以在脚本补充一段 echo $0 $*

最后对于 Linux/Unix、OS X,还需要增加脚本的可执行权限:

1
$ chmod +x ~/meld.sh

以上配置好后,就可以调用图形化工具愉快的使用 git diff 了。

用于 git mergetool

首先配置好 git :

1
$ git config --global merge.tool meld

如果合并的时候出现如下冲突:

1
CONFLICT (content): Merge conflict in index.html Automatic merge failed; fix conflicts and then commit the result.

使用如下命令:

1
$ git mergetool

就可以调用图形化工具愉快的解决冲突了。

在解决了所有文件的所有冲突后,运行 git add 将把它们标记为已解决状态即可(一旦暂存,就表示冲突已经解决)。

参考

如果工作目录的本地代码做了修改但尚未提交,pull 拉取远程仓库的新提交时,往往会提示冲突:

1
2
3
4
$ git pull
error: Your local changes to the following files would be overwritten by merge:
/there/is/a/conflict/file
Please, commit your changes or stash them before you can merge.

如上所示,有 commitstash 两种处理方法。针对本地代码的完成情况我们需要作出选择。

代码已完成

如果确认本地代码已经完成无误,可以先将本地代码 commit 到本地仓库。再次 pull 拉取远程仓库时,如无冲突,Git 会自动产生一次“合并”提交:

1
2
3
4
5
6
7
$ git lg
* e6f4e18 - Merge branch 'master' of origin (1 minutes ago)
|\
* | abfa93b - 本地仓库的提交 (2 minutes ago)
| * 1f1c21d - 远程仓库的提交 (3 minutes ago)
|/
* 17ef24c - 基准版本 (4 minutes ago)

这是因为 pull 的默认策略是“fetch + merge”。如果本地仓库的提交一直不 push 到远程仓库,极端情况下每一次 pull 都可能会产生一次“合并”提交,这会造成祖先图谱(graph)无谓的复杂。此时推荐使用 rebase 避免本地仓库无谓的合并节点:

1
$ git pull --rebase

代码未完成

但如果本地代码仍未完成,此时推荐使用 stash 命令暂存修改,避免将未完成的功能代码 commit 到本地仓库,污染仓库。

暂存当前修改

第一步,stash 暂存当前修改:

1
2
$ git stash save "填写你的备注"
Saved working directory and index state ......
1
2
3
$ git status
On branch master
nothing to commit, working directory clean

可以看到暂存后工作目录一干二净。这是因为 stash 命令可以将“修改过的被追踪的文件(modified tracked files)”和“暂存的变更(staged changes)”暂存到临时堆栈中,并将工作目录还原干净,以便后续的操作。

Stashing takes the dirty state of your working directory – that is, your modified tracked files and staged changes – and saves it on a stack of unfinished changes that you can reapply at any time.

拉取远程仓库

第二步,继续 pull 拉取远程仓库并进行自动合并:

1
$ git pull

重新还原暂存

第三步,stash pop 重新还原暂存修改:

1
$ git stash pop

处理冲突

最后一步,如果还原后产生冲突,需要手工或使用 mergetool 进行处理。处理完毕后,使用 add 标明冲突已解决:

1
$ git add .

参考

Git-工具-储藏(Stashing)