<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>07sec</title>
  
  <subtitle>I&#39;m curious.</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="https://www.07sec.com/"/>
  <updated>2022-04-03T10:30:13.019Z</updated>
  <id>https://www.07sec.com/</id>
  
  <author>
    <name>Sean Fang</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>Linux 基础知识</title>
    <link href="https://www.07sec.com/2018/04/03/Linux/"/>
    <id>https://www.07sec.com/2018/04/03/Linux/</id>
    <published>2022-04-03T07:53:30.000Z</published>
    <updated>2022-04-03T10:30:13.019Z</updated>
    
    <content type="html"><![CDATA[<h1 id="一、常用操作以及概念"><a href="#一、常用操作以及概念" class="headerlink" title="一、常用操作以及概念"></a>一、常用操作以及概念</h1><h2 id="求助"><a href="#求助" class="headerlink" title="求助"></a>求助</h2><h3 id="1-–help"><a href="#1-–help" class="headerlink" title="1. –help"></a>1. –help</h3><p>指令的基本用法与选项介绍。</p><h3 id="2-man"><a href="#2-man" class="headerlink" title="2. man"></a>2. man</h3><p>man 是 manual 的缩写，将指令的具体信息显示出来。</p><p>当执行 man date 时，有 DATE(1) 出现，其中的数字代表指令的类型，常用的数字及其类型如下：</p><table><thead><tr><th style="text-align:center">代号</th><th>类型</th></tr></thead><tbody><tr><td style="text-align:center">1</td><td>用户在 shell 环境中可以操作的指令或者可执行文件</td></tr><tr><td style="text-align:center">5</td><td>配置文件</td></tr><tr><td style="text-align:center">8</td><td>系统管理员可以使用的管理指令</td></tr></tbody></table><h3 id="3-info"><a href="#3-info" class="headerlink" title="3. info"></a>3. info</h3><p>info 与 man 类似，但是 info 将文档分成一个个页面，每个页面可以进行跳转。</p><h2 id="关机"><a href="#关机" class="headerlink" title="关机"></a>关机</h2><h3 id="1-sync"><a href="#1-sync" class="headerlink" title="1. sync"></a>1. sync</h3><p>为了加快对磁盘文件的读写速度，位于内存中的文件数据不会立即同步到磁盘上，因此关机之前需要先进行 sync 同步操作。</p><h3 id="2-shutdown"><a href="#2-shutdown" class="headerlink" title="2. shutdown"></a>2. shutdown</h3><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"># /sbin/shutdown [-krhc] [时间] [警告讯息]</span><br><span class="line">-k ： 不会关机，只是发送警告讯息，通知所有在线的用户</span><br><span class="line">-r ： 将系统的服务停掉后就重新启动</span><br><span class="line">-h ： 将系统的服务停掉后就立即关机</span><br><span class="line">-c ： 取消已经在进行的 shutdown 指令内容</span><br></pre></td></tr></table></figure><h3 id="3-其它关机指令"><a href="#3-其它关机指令" class="headerlink" title="3. 其它关机指令"></a>3. 其它关机指令</h3><p>reboot、halt、poweroff。</p><h2 id="PATH"><a href="#PATH" class="headerlink" title="PATH"></a>PATH</h2><p>可以在环境变量 PATH 中声明可执行文件的路径，路径之间用 : 分隔。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/dmtsai/.local/bin:/home/dmtsai/bin</span><br></pre></td></tr></table></figure><h2 id="运行等级"><a href="#运行等级" class="headerlink" title="运行等级"></a>运行等级</h2><ul><li>0：关机模式</li><li>1：单用户模式（可用于破解 root 密码）</li><li>2：无网络支持的多用户模式</li><li>3：有网络支持的多用户模式（文本模式，工作中最常用的模式）</li><li>4：保留，未使用</li><li>5：有网络支持的 X-windows 多用户模式（桌面）</li><li>6：重新引导系统，即重启</li></ul><h2 id="sudo"><a href="#sudo" class="headerlink" title="sudo"></a>sudo</h2><p>使用 sudo 允许一般用户使用 root 可执行的命令，只有在 /etc/sudoers 配置文件中添加的用户才能使用该指令。</p><h2 id="GNU"><a href="#GNU" class="headerlink" title="GNU"></a>GNU</h2><p>GNU 计划，译为革奴计划，它的目标是创建一套完全自由的操作系统，称为 GNU，其内容软件完全以 GPL 方式发布。其中 GPL 全称为 GNU 通用公共许可协议，包含了以下内容：</p><ul><li>以任何目的运行此程序的自由；</li><li>再复制的自由；</li><li>改进此程序，并公开发布改进的自由。</li></ul><h2 id="包管理工具"><a href="#包管理工具" class="headerlink" title="包管理工具"></a>包管理工具</h2><p>RPM 和 DPKG 为最常见的两类软件包管理工具。RPM 全称为 Redhat Package Manager，最早由 Red Hat 公司制定实施，随后被 GNU 开源操作系统接受并成为很多 Linux 系统 (RHEL) 的既定软件标准。与 RPM 进行竞争的是基于 Debian 操作系统 (UBUNTU) 的 DEB 软件包管理工具－ DPKG，全称为 Debian Package，功能方面与 RPM 相似。</p><p>YUM 基于 RPM 包管理工具，具有依赖管理功能，并具有软件升级的功能。</p><h2 id="发行版"><a href="#发行版" class="headerlink" title="发行版"></a>发行版</h2><p>Linux 发行版是 Linux 内核及各种应用软件的集成版本。</p><table><thead><tr><th>基于的包管理工具</th><th>商业发行版</th><th>社区发行版</th></tr></thead><tbody><tr><td>DPKG</td><td>Ubuntu</td><td>Debian</td></tr><tr><td>RPM</td><td>Red Hat</td><td>Fedora / CentOS</td></tr></tbody></table><h2 id="VIM-三个模式"><a href="#VIM-三个模式" class="headerlink" title="VIM 三个模式"></a>VIM 三个模式</h2><ul><li>一般指令模式（Command mode）：进入 VIM 的默认模式，可以用于移动游标查看内容；</li><li>编辑模式（Insert mode）：按下 “i” 等按键之后进入，可以对文本进行编辑；</li><li>指令列模式（Bottom-line mode）：按下 “:” 按键之后进入，用于保存退出等操作。</li></ul><p><div align="center"> <img src="https://poo1.club/img/5942debd-fc00-477a-b390-7c5692cc8070.jpg" width="400"> </div><br></p><p>在指令列模式下，有以下命令用于离开或者保存文件。</p><table><thead><tr><th style="text-align:center">命令</th><th>作用</th></tr></thead><tbody><tr><td style="text-align:center">:w</td><td>写入磁盘</td></tr><tr><td style="text-align:center">:w!</td><td>当文件为只读时，强制写入磁盘。到底能不能写入，与用户对该文件的权限有关</td></tr><tr><td style="text-align:center">:q</td><td>离开</td></tr><tr><td style="text-align:center">:q!</td><td>强制离开不保存</td></tr><tr><td style="text-align:center">:wq</td><td>写入磁盘后离开</td></tr><tr><td style="text-align:center">:wq!</td><td>强制写入磁盘后离开</td></tr></tbody></table><h1 id="二、分区"><a href="#二、分区" class="headerlink" title="二、分区"></a>二、分区</h1><h2 id="磁盘的文件名"><a href="#磁盘的文件名" class="headerlink" title="磁盘的文件名"></a>磁盘的文件名</h2><p>Linux 中每个硬件都被当做一个文件，包括磁盘。常见磁盘的文件名如下：</p><ul><li>SCSI/SATA/USB 磁盘：/dev/sd[a-p]</li><li>IDE 磁盘：/dev/hd[a-d]</li></ul><p>其中文件名后面的序号的确定与磁盘插入的顺序有关，而与磁盘所插入的插槽位置无关。</p><h2 id="分区表"><a href="#分区表" class="headerlink" title="分区表"></a>分区表</h2><p>磁盘分区表主要有两种格式，一种是限制较多的 MBR 分区表，一种是较新且限制较少的 GPT 分区表。</p><h3 id="1-MBR"><a href="#1-MBR" class="headerlink" title="1. MBR"></a>1. MBR</h3><p>MBR 中，第一个扇区最重要，里面有主要开机记录（Master boot record, MBR）及分区表（partition table），其中 MBR 占 446 bytes，分区表占 64 bytes。</p><p>分区表只有 64 bytes，最多只能存储 4 个分区，这 4 个分区为主分区（Primary）和扩展分区（Extended）。其中扩展分区只有一个，它将其它空间用来记录分区表，因此通过扩展分区可以分出更多分区，这些分区称为逻辑分区。</p><p>Linux 也把分区当成文件，分区文件的命名方式为：磁盘文件名 + 编号，例如 /dev/sda1。注意，逻辑分区的编号从 5 开始。</p><h3 id="2-GPT"><a href="#2-GPT" class="headerlink" title="2. GPT"></a>2. GPT</h3><p>不同的磁盘有不同的扇区大小，例如 512 bytes 和最新磁盘的 4 k。GPT 为了兼容所有磁盘，在定义扇区上使用逻辑区块地址（Logical Block Address, LBA）。</p><p>GPT 第 1 个区块记录了 MBR，紧接着是 33 个区块记录分区信息，并把最后的 33 个区块用于对分区信息进行备份。</p><p>GPT 没有扩展分区概念，都是主分区，最多可以分 128 个分区。</p><p><div align="center"> <img src="https://poo1.club/img/GUID_Partition_Table_Scheme.svg.png" width="400"> </div><br></p><h2 id="开机检测程序"><a href="#开机检测程序" class="headerlink" title="开机检测程序"></a>开机检测程序</h2><h3 id="1-BIOS"><a href="#1-BIOS" class="headerlink" title="1. BIOS"></a>1. BIOS</h3><p>BIOS 是开机的时候计算机执行的第一个程序，这个程序知道可以开机的磁盘，并读取磁盘第一个扇区的 MBR，由 MBR 执行其中的开机管理程序，这个开机管理程序会加载操作系统的核心文件。</p><p>MBR 中的开机管理程序提供以下功能：选单、载入核心文件以及转交其它开机管理程序。转交这个功能可以用来实现了多重引导，只需要将另一个操作系统的开机管理程序安装在其它分区的启动扇区上，在启动 MBR 中的开机管理程序时，就可以选择启动当前的操作系统或者转交给其它开机管理程序从而启动另一个操作系统。</p><p>安装多重引导，最好先安装 Windows 再安装 Linux。因为安装 Windows 时会覆盖掉 MBR，而 Linux 可以选择将开机管理程序安装在 MBR 或者其它分区的启动扇区，并且可以设置开机管理程序的选单。</p><p><div align="center"> <img src="https://poo1.club/img/f900f266-a323-42b2-bc43-218fdb8811a8.jpg" width="600"> </div><br></p><h3 id="2-UEFI"><a href="#2-UEFI" class="headerlink" title="2. UEFI"></a>2. UEFI</h3><p>UEFI 相比于 BIOS 来说功能更为全面，也更为安全。</p><h2 id="挂载"><a href="#挂载" class="headerlink" title="挂载"></a>挂载</h2><p>挂载利用目录作为分区的进入点，也就是说，进入目录之后就可以读取分区的数据。</p><p><div align="center"> <img src="https://poo1.club/img/249f3bb1-feee-4805-a259-a72699d638ca.jpg" width="500"> </div><br></p><h1 id="三、文件"><a href="#三、文件" class="headerlink" title="三、文件"></a>三、文件</h1><h2 id="文件权限概念"><a href="#文件权限概念" class="headerlink" title="文件权限概念"></a>文件权限概念</h2><p>把用户分为三种：文件拥有者、群组以及其它人，对不同的用户有不同的文件权限。</p><p>使用 ls 查看一个文件时，会显示一个文件的信息，例如 <code>drwxr-xr-x. 3 root root 17 May 6 00:14 .config</code>，对这个信息的解释如下：</p><ul><li>drwxr-xr-x：文件类型以及权限，第 1 位为文件类型字段，后 9 位为文件权限字段。</li><li>3：链接数；</li><li>root：文件拥有者；</li><li>root：所属群组；</li><li>17：文件大小；</li><li>May 6 00:14：文件最后被修改的时间；</li><li>.config：文件名。</li></ul><p>常见的文件类型及其含义有：</p><ul><li>d：目录；</li><li>-：文件；</li><li>l：链接文件；</li></ul><p>9 位的文件权限字段中，每 3 个为一组，共 3 组，每一组分别代表对文件拥有者、所属群组以及其它人的文件权限。一组权限中的 3 位分别为 r、w、x 权限，表示可读、可写、可执行。</p><h2 id="文件属性以及权限的修改"><a href="#文件属性以及权限的修改" class="headerlink" title="文件属性以及权限的修改"></a>文件属性以及权限的修改</h2><h3 id="1-修改文件所属群组"><a href="#1-修改文件所属群组" class="headerlink" title="1. 修改文件所属群组"></a>1. 修改文件所属群组</h3><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"># chgrp [-R] groupname dirname/filename</span><br><span class="line">-R：递归修改</span><br></pre></td></tr></table></figure><h3 id="2-修改文件拥有者"><a href="#2-修改文件拥有者" class="headerlink" title="2. 修改文件拥有者"></a>2. 修改文件拥有者</h3><p>不仅可以修改文件拥有者，也可以修改文件所属群组。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"># chown [-R] 用户名:群组名 dirname/filename</span><br></pre></td></tr></table></figure><h3 id="3-修改权限"><a href="#3-修改权限" class="headerlink" title="3. 修改权限"></a>3. 修改权限</h3><p>可以将一组权限用数字来表示，此时一组权限的 3 个位当做二进制数字的位，从左到右每个位的权值为 4、2、1，即每个权限对应的数字权值为 r : 4、w : 2、x : 1。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"># chmod [-R] xyz dirname/filename</span><br></pre></td></tr></table></figure><p>范例：将 .bashrc 文件的权限修改为 -rwxr-xr–。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"># chmod 754 .bashrc</span><br></pre></td></tr></table></figure><p>也可以使用符号来设定权限。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"># chmod [ugoa]  [+-=] [rwx] dirname/filename</span><br><span class="line">- u：拥有者</span><br><span class="line">- g：所属群组</span><br><span class="line">- o：其他人</span><br><span class="line">- a：所有人</span><br><span class="line">- +：添加权限</span><br><span class="line">- -：移除权限</span><br><span class="line">- =：设定权限</span><br></pre></td></tr></table></figure><p>范例：为 .bashrc 文件的所有用户添加写权限。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"># chmod a+w .bashrc</span><br></pre></td></tr></table></figure><h2 id="目录的权限"><a href="#目录的权限" class="headerlink" title="目录的权限"></a>目录的权限</h2><p>文件名不是存储在一个文件的内容中，而是存储在一个文件所在的目录中。因此，拥有文件的 w 权限并不能对文件名进行修改。</p><p>目录存储文件列表，一个目录的权限也就是对其文件列表的权限。因此，目录的 r 权限表示可以读取文件列表；w 权限表示可以修改文件列表，具体来说，就是添加删除文件，对文件名进行修改；x 权限可以让该目录成为工作目录，x 权限是 r 和 w 权限的基础，如果不能使一个目录成为工作目录，也就没办法读取文件列表以及对文件列表进行修改了。</p><h2 id="文件默认权限"><a href="#文件默认权限" class="headerlink" title="文件默认权限"></a>文件默认权限</h2><ul><li>文件默认权限：文件默认没有可执行权限，因此为 666，也就是 -rw-rw-rw- 。</li><li>目录默认权限：目录必须要能够进入，也就是必须拥有可执行权限，因此为 777 ，也就是 drwxrwxrwx。</li></ul><p>可以通过 umask 设置或者查看文件的默认权限，通常以掩码的形式来表示，例如 002 表示其它用户的权限去除了一个 2 的权限，也就是写权限，因此建立新文件时默认的权限为 -rw-rw-r– 。</p><h2 id="目录配置"><a href="#目录配置" class="headerlink" title="目录配置"></a>目录配置</h2><p>为了使不同 Linux 发行版本的目录结构保持一致性，Filesystem Hierarchy Standard (FHS) 规定了 Linux 的目录结构。最基础的三个目录如下：</p><ul><li>/ (root, 根目录)</li><li>/usr (unix software resource)：所有系统默认软件都会安装到这个目录；</li><li>/var (variable)：存放系统或程序运行过程中的数据文件。</li></ul><p><div align="center"> <img src="https://poo1.club/img/linux-filesystem.png" width=""> </div><br></p><h2 id="文件时间"><a href="#文件时间" class="headerlink" title="文件时间"></a>文件时间</h2><ol><li>modification time (mtime)：文件的内容更新就会更新；</li><li>status time (ctime)：文件的状态（权限、属性）更新就会更新；</li><li>access time (atime)：读取文件时就会更新。</li></ol><h2 id="文件与目录的基本操作"><a href="#文件与目录的基本操作" class="headerlink" title="文件与目录的基本操作"></a>文件与目录的基本操作</h2><h3 id="1-ls"><a href="#1-ls" class="headerlink" title="1. ls"></a>1. ls</h3><p>列出文件或者目录的信息，目录的信息就是其中包含的文件。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"># ls [-aAdfFhilnrRSt] file|dir</span><br><span class="line">-a ：列出全部的文件</span><br><span class="line">-d ：仅列出目录本身</span><br><span class="line">-l ：以长数据串行列出，包含文件的属性与权限等等数据</span><br></pre></td></tr></table></figure><h3 id="2-cp"><a href="#2-cp" class="headerlink" title="2. cp"></a>2. cp</h3><p>复制操作。</p><p>如果源文件有两个以上，则目的文件一定要是目录才行。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">cp [-adfilprsu] source destination</span><br><span class="line">-a ：相当于 -dr --preserve=all 的意思，至于 dr 请参考下列说明</span><br><span class="line">-d ：若来源文件为链接文件，则复制链接文件属性而非文件本身</span><br><span class="line">-i ：若目标文件已经存在时，在覆盖前会先询问</span><br><span class="line">-p ：连同文件的属性一起复制过去</span><br><span class="line">-r ：递归持续复制</span><br><span class="line">-u ：destination 比 source 旧才更新 destination，或 destination 不存在的情况下才复制</span><br><span class="line">--preserve=all ：除了 -p 的权限相关参数外，还加入 SELinux 的属性, links, xattr 等也复制了</span><br></pre></td></tr></table></figure><h3 id="3-rm"><a href="#3-rm" class="headerlink" title="3. rm"></a>3. rm</h3><p>移除操作。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"># rm [-fir] 文件或目录</span><br><span class="line">-r ：递归删除</span><br></pre></td></tr></table></figure><h3 id="4-mv"><a href="#4-mv" class="headerlink" title="4. mv"></a>4. mv</h3><p>移动操作。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"># mv [-fiu] source destination</span><br><span class="line"># mv [options] source1 source2 source3 .... directory</span><br><span class="line">-f ： force 强制的意思，如果目标文件已经存在，不会询问而直接覆盖</span><br></pre></td></tr></table></figure><h2 id="获取文件内容"><a href="#获取文件内容" class="headerlink" title="获取文件内容"></a>获取文件内容</h2><h3 id="1-cat"><a href="#1-cat" class="headerlink" title="1. cat"></a>1. cat</h3><p>取得文件内容。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"># cat [-AbEnTv] filename</span><br><span class="line">-n ：打印出行号，连同空白行也会有行号，-b 不会</span><br></pre></td></tr></table></figure><h3 id="2-tac"><a href="#2-tac" class="headerlink" title="2. tac"></a>2. tac</h3><p>是 cat 的反向操作，从最后一行开始打印。</p><h3 id="3-more"><a href="#3-more" class="headerlink" title="3. more"></a>3. more</h3><p>可以一页一页查看文件内容，和文本编辑器类似。</p><h3 id="4-less"><a href="#4-less" class="headerlink" title="4. less"></a>4. less</h3><p>和 more 类似。</p><h3 id="5-head"><a href="#5-head" class="headerlink" title="5. head"></a>5. head</h3><p>可以取得文件前几行。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"># head [-n number] filename</span><br><span class="line">-n ：后面接数字，代表显示几行的意思</span><br></pre></td></tr></table></figure><h3 id="6-tail"><a href="#6-tail" class="headerlink" title="6. tail"></a>6. tail</h3><p>是 head 的反向操作，只是取得是后几行。</p><h3 id="7-od"><a href="#7-od" class="headerlink" title="7. od"></a>7. od</h3><p>可以以字符或者十六进制的形式显示二进制文件。</p><h3 id="8-touch"><a href="#8-touch" class="headerlink" title="8. touch"></a>8. touch</h3><p>修改文件时间或者建立新文件。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"># touch [-acdmt] filename</span><br><span class="line">-a ： 更新 atime</span><br><span class="line">-c ： 更新 ctime，若该文件不存在则不建立新文件</span><br><span class="line">-m ： 更新 mtime</span><br><span class="line">-d ： 后面可以接更新日期而不使用当前日期，也可以使用 --date="日期或时间"</span><br><span class="line">-t ： 后面可以接更新时间而不使用当前时间，格式为[YYYYMMDDhhmm]</span><br></pre></td></tr></table></figure><h2 id="指令与文件搜索"><a href="#指令与文件搜索" class="headerlink" title="指令与文件搜索"></a>指令与文件搜索</h2><h3 id="1-which"><a href="#1-which" class="headerlink" title="1. which"></a>1. which</h3><p>指令搜索。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"># which [-a] command</span><br><span class="line">-a ：将所有指令列出，而不是只列第一个</span><br></pre></td></tr></table></figure><h3 id="2-whereis"><a href="#2-whereis" class="headerlink" title="2. whereis"></a>2. whereis</h3><p>whereis 搜索文件的速度比较快，因为它只搜索几个特定的目录。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"># whereis [-bmsu] dirname/filename</span><br></pre></td></tr></table></figure><h3 id="3-locate"><a href="#3-locate" class="headerlink" title="3. locate"></a>3. locate</h3><p>locate 可以用关键字或者正则表达式进行搜索。</p><p>locate 使用 /var/lib/mlocate/ 这个数据库来进行搜索，它存储在内存中，并且每天更新一次，所以无法用 locate 搜索新建的文件。可以使用 updatedb 来立即更新数据库。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"># locate [-ir] keyword</span><br><span class="line">-r：接正则表达式</span><br></pre></td></tr></table></figure><h3 id="4-find"><a href="#4-find" class="headerlink" title="4. find"></a>4. find</h3><p>find 可以使用文件的属性和权限进行搜索。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"># find filename [option]</span><br></pre></td></tr></table></figure><p>（一）与时间有关的选项</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">-mtime  n ：列出在 n 天前的那一天修改过内容的文件</span><br><span class="line">-mtime +n ：列出在 n 天之前 (不含 n 天本身) 修改过内容的文件</span><br><span class="line">-mtime -n ：列出在 n 天之内 (含 n 天本身) 修改过内容的文件</span><br><span class="line">-newer file ： 列出比 file 更新的文件</span><br></pre></td></tr></table></figure><p>+4、4 和 -4 的指示的时间范围如下：</p><p><div align="center"> <img src="https://poo1.club/img/658fc5e7-79c0-4247-9445-d69bf194c539.png" width=""> </div><br></p><p>（二）与文件拥有者和所属群组有关的选项</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">-uid n</span><br><span class="line">-gid n</span><br><span class="line">-user name</span><br><span class="line">-group name</span><br><span class="line">-nouser ：搜索拥有者不存在 /etc/passwd 的文件</span><br><span class="line">-nogroup：搜索所属群组不存在于 /etc/group 的文件</span><br></pre></td></tr></table></figure><p>（三）与文件权限和名称有关的选项</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">-name filename</span><br><span class="line">-size [+-]SIZE：搜寻比 SIZE 还要大 (+) 或小 (-) 的文件。这个 SIZE 的规格有：c: 代表 byte，k: 代表 1024bytes。所以，要找比 50KB 还要大的文件，就是 -size +50k</span><br><span class="line">-type TYPE</span><br><span class="line">-perm mode  ：搜索权限等于 mode 的文件</span><br><span class="line">-perm -mode ：搜索权限包含 mode 的文件</span><br><span class="line">-perm /mode ：搜索权限包含任一 mode 的文件</span><br></pre></td></tr></table></figure><h1 id="四、磁盘与文件系统"><a href="#四、磁盘与文件系统" class="headerlink" title="四、磁盘与文件系统"></a>四、磁盘与文件系统</h1><h2 id="文件系统的组成"><a href="#文件系统的组成" class="headerlink" title="文件系统的组成"></a>文件系统的组成</h2><p>对分区进行格式化是为了在分区上建立文件系统。一个分区通常只能格式化为一个文件系统，但是磁盘阵列等技术可以将一个分区格式化为多个文件系统，因此只有文件系统能被挂载，而分区不能被挂载。</p><p>文件系统有以下三个结构：</p><ol><li>superblock：记录文件系统的整体信息，包括 inode 和 block 的总量、使用量、剩余量，以及文件系统的格式与相关信息等；</li><li>inode：一个文件占用一个 inode，记录文件的属性，同时记录此文件的内容所在的 block 号码；</li><li>block：记录文件的内容，文件太大时，会占用多个 block。</li></ol><p><div align="center"> <img src="https://poo1.club/img/ff0c019c-6461-467d-a266-0455341fd4f4.png" width="800"> </div><br></p><p>当要读取一个文件的内容时，先在 inode 中去查找文件内容所在的所有 block，然后把所有 block 的内容读出来。</p><p>磁盘碎片是指一个文件内容所在的 block 过于分散。</p><h2 id="inode"><a href="#inode" class="headerlink" title="inode"></a>inode</h2><p>Ext2 文件系统支持的 block 大小有 1k、2k 和 4k 三种，不同的 block 大小限制了单一文件的大小。而每个 inode 大小是固定为 128 bytes。</p><p>inode 中记录了文件内容所在的 block，但是每个 block 非常小，一个大文件随便都需要几十万的 block。而一个 inode 大小有限，无法直接引用这么多 block。因此引入了间接、双间接、三间接引用。间接引用是指，让 inode 记录的引用 block 块当成 inode 用来记录引用信息。</p><p><div align="center"> <img src="https://poo1.club/img/1bfa3118-f3cd-4480-a950-cf6d646015db.png" width="600"> </div><br></p><p>inode 具体包含以下信息：</p><ul><li>该文件的存取模式 (read/write/excute)；</li><li>该文件的拥有者与群组 (owner/group)；</li><li>该文件的容量；</li><li>该文件建立或状态改变的时间 (ctime)；</li><li>最近一次的读取时间 (atime)；</li><li>最近修改的时间 (mtime)；</li><li>定义文件特性的旗标 (flag)，如 SetUID…；</li><li>该文件真正内容的指向 (pointer)。</li></ul><h2 id="目录的-inode-与-block"><a href="#目录的-inode-与-block" class="headerlink" title="目录的 inode 与 block"></a>目录的 inode 与 block</h2><p>建立一个目录时，会分配一个 inode 与至少一个 block。block 记录的内容是目录下所有文件的 inode 编号以及文件名。可以看出文件的 inode 本身不记录文件名，文件名记录在目录中，因此新增文件、删除文件、更改文件名这些操作与目录的 w 权限有关。</p><h2 id="实体链接与符号链接"><a href="#实体链接与符号链接" class="headerlink" title="实体链接与符号链接"></a>实体链接与符号链接</h2><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"># ln [-sf] source_filename dist_filename</span><br><span class="line">-s ：默认是 hard link，加 -s 为 symbolic link</span><br><span class="line">-f ：如果目标文件存在时，先删除目标文件</span><br></pre></td></tr></table></figure><h3 id="1-实体链接"><a href="#1-实体链接" class="headerlink" title="1. 实体链接"></a>1. 实体链接</h3><p>它和普通文件类似，实体链接文件的 inode 都指向源文件所在的 block 上，也就是说读取文件直接从源文件的 block 上读取。</p><p>删除任意一个条目，文件还是存在，只要引用数量不为 0。</p><p>有以下限制：不能跨越 File System、不能对目录进行链接。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"># ln /etc/crontab .</span><br><span class="line"># ll -i /etc/crontab crontab</span><br><span class="line">34474855 -rw-r--r--. 2 root root 451 Jun 10 2014 crontab</span><br><span class="line">34474855 -rw-r--r--. 2 root root 451 Jun 10 2014 /etc/crontab</span><br></pre></td></tr></table></figure><h3 id="2-符号链接"><a href="#2-符号链接" class="headerlink" title="2. 符号链接"></a>2. 符号链接</h3><p>符号链接文件保存着源文件所在的绝对路径，在读取时会定位到源文件上，可以理解为 Windows 的快捷方式。</p><p>当源文件被删除了，链接文件就打不开了。</p><p>可以为目录建立链接。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"># ll -i /etc/crontab /root/crontab2</span><br><span class="line">34474855 -rw-r--r--. 2 root root 451 Jun 10 2014 /etc/crontab</span><br><span class="line">53745909 lrwxrwxrwx. 1 root root 12 Jun 23 22:31 /root/crontab2 -&gt; /etc/crontab</span><br></pre></td></tr></table></figure><h1 id="五、压缩与打包"><a href="#五、压缩与打包" class="headerlink" title="五、压缩与打包"></a>五、压缩与打包</h1><h2 id="压缩"><a href="#压缩" class="headerlink" title="压缩"></a>压缩</h2><p>Linux 底下有很多压缩文件的扩展名，常见的如下：</p><table><thead><tr><th>扩展名</th><th>压缩程序</th></tr></thead><tbody><tr><td>*.Z</td><td>compress</td></tr><tr><td>*.zip</td><td>zip</td></tr><tr><td>*.gz</td><td>gzip</td></tr><tr><td>*.bz2</td><td>bzip2</td></tr><tr><td>*.xz</td><td>xz</td></tr><tr><td>*.tar</td><td>tar 程序打包的数据，没有经过压缩</td></tr><tr><td>*.tar.gz</td><td>tar 程序打包的文件，经过 gzip 的压缩</td></tr><tr><td>*.tar.bz2</td><td>tar 程序打包的文件，经过 bzip2 的压缩</td></tr><tr><td>*.tar.xz</td><td>tar 程序打包的文件，经过 xz 的压缩</td></tr></tbody></table><h3 id="1-gzip"><a href="#1-gzip" class="headerlink" title="1. gzip"></a>1. gzip</h3><p>gzip 是 Linux 使用最广的压缩指令，可以解开 compress、zip 与 gzip 所压缩的文件。</p><p>经过 gzip 压缩过，源文件就不存在了。</p><p>有 9 个不同的压缩等级可以使用。</p><p>可以使用 zcat、zmore、zless 来读取压缩文件的内容。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">$ gzip [-cdtv#] filename</span><br><span class="line">-c ：将压缩的数据输出到屏幕上</span><br><span class="line">-d ：解压缩</span><br><span class="line">-t ：检验压缩文件是否出错</span><br><span class="line">-v ：显示压缩比等信息</span><br><span class="line">-# ： # 为数字的意思，代表压缩等级，数字越大压缩比越高，默认为 6</span><br></pre></td></tr></table></figure><h3 id="2-bzip2"><a href="#2-bzip2" class="headerlink" title="2. bzip2"></a>2. bzip2</h3><p>提供比 gzip 更高的压缩比。</p><p>查看命令：bzcat、bzmore、bzless、bzgrep。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ bzip2 [-cdkzv#] filename</span><br><span class="line">-k ：保留源文件</span><br></pre></td></tr></table></figure><h3 id="3-xz"><a href="#3-xz" class="headerlink" title="3. xz"></a>3. xz</h3><p>提供比 bzip2 更佳的压缩比。</p><p>可以看到，gzip、bzip2、xz 的压缩比不断优化。不过要注意的是，压缩比越高，压缩的时间也越长。</p><p>查看命令：xzcat、xzmore、xzless、xzgrep。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ xz [-dtlkc#] filename</span><br></pre></td></tr></table></figure><h2 id="打包"><a href="#打包" class="headerlink" title="打包"></a>打包</h2><p>压缩指令只能对一个文件进行压缩，而打包能够将多个文件打包成一个大文件。tar 不仅可以用于打包，也可以使用 gip、bzip2、xz 将打包文件进行压缩。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">$ tar [-z|-j|-J] [cv] [-f 新建的 tar 文件] filename...  ==打包压缩</span><br><span class="line">$ tar [-z|-j|-J] [tv] [-f 已有的 tar 文件]              ==查看</span><br><span class="line">$ tar [-z|-j|-J] [xv] [-f 已有的 tar 文件] [-C 目录]    ==解压缩</span><br><span class="line">-z ：使用 zip；</span><br><span class="line">-j ：使用 bzip2；</span><br><span class="line">-J ：使用 xz；</span><br><span class="line">-c ：新建打包文件；</span><br><span class="line">-t ：查看打包文件里面有哪些文件；</span><br><span class="line">-x ：解打包或解压缩的功能；</span><br><span class="line">-v ：在压缩/解压缩的过程中，显示正在处理的文件名；</span><br><span class="line">-f : filename：要处理的文件；</span><br><span class="line">-C 目录 ： 在特定目录解压缩。</span><br></pre></td></tr></table></figure><table><thead><tr><th>使用方式</th><th>命令</th></tr></thead><tbody><tr><td>打包压缩</td><td>tar -jcv -f filename.tar.bz2 要被压缩的文件或目录名称</td></tr><tr><td>查 看</td><td>tar -jtv -f filename.tar.bz2</td></tr><tr><td>解压缩</td><td>tar -jxv -f filename.tar.bz2 -C 要解压缩的目录</td></tr></tbody></table><h1 id="六、Bash"><a href="#六、Bash" class="headerlink" title="六、Bash"></a>六、Bash</h1><p>可以通过 Shell 请求内核提供服务，Bash 正是 Shell 的一种。</p><h2 id="特性"><a href="#特性" class="headerlink" title="特性"></a>特性</h2><ol><li><p>命令历史：记录使用过的命令。本次登录所执行的命令都会暂时存放到内存中，~/.bash_history 文件中记录的是前一次登录所执行过的命令。</p></li><li><p>命令与文件补全：快捷键：tab。</p></li><li><p>命名别名：例如 lm 是 ls -al 的别名。</p></li><li><p>shell scripts。</p></li><li><p>通配符：例如 ls -l /usr/bin/X* 列出 /usr/bin 下面所有以 X 开头的文件。</p></li></ol><h2 id="变量操作"><a href="#变量操作" class="headerlink" title="变量操作"></a>变量操作</h2><ul><li>对一个变量赋值直接使用 = ；</li><li>对变量取用需要在变量前加上 \$ ，也可以用 \${} 的形式；</li><li>输出变量使用 echo 命令。</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ var=abc</span><br><span class="line">$ <span class="built_in">echo</span> <span class="variable">$var</span></span><br><span class="line">$ <span class="built_in">echo</span> <span class="variable">$&#123;var&#125;</span></span><br></pre></td></tr></table></figure><p>变量内容如果有空格，必须需要使用双引号或者单引号。</p><ul><li>双引号内的特殊字符可以保留原本特性，例如 var=”lang is \$LANG”，则 var 的值为 lang is zh_TW.UTF-8；</li><li>单引号内的特殊字符就是特殊字符本身，例如 var=’lang is \$LANG’，则 var 的值为 lang is \$LANG。</li></ul><p>可以使用 `指令` 或者 \$(指令) 的方式将指令的执行结果赋值给变量。例如 version=\$(uname -r)，则 version 的值为 3.10.0-229.el7.x86_64。</p><p>可以使用 export 命令将自定义变量转成环境变量，环境变量可以在子程序中使用，所谓子程序就是由当前 Bash 而产生的子 Bash。</p><p>Bash 的变量可以声明为数组和整数数字。注意数字类型没有浮点数。如果不进行声明，默认是字符串类型。变量的声明使用 declare 命令：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">$ declare [-aixr] variable</span><br><span class="line">-a ： 定义为数组类型</span><br><span class="line">-i ： 定义为整数类型</span><br><span class="line">-x ： 定义为环境变量</span><br><span class="line">-r ： 定义为 readonly 类型</span><br></pre></td></tr></table></figure><p>使用 [ ] 来对数组进行索引操作：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ array[1]=a</span><br><span class="line">$ array[2]=b</span><br><span class="line">$ <span class="built_in">echo</span> <span class="variable">$&#123;array[1]&#125;</span></span><br></pre></td></tr></table></figure><h2 id="指令搜索顺序"><a href="#指令搜索顺序" class="headerlink" title="指令搜索顺序"></a>指令搜索顺序</h2><ol><li>以绝对或相对路径来执行指令，例如 /bin/ls 或者 ./ls ；</li><li>由别名找到该指令来执行；</li><li>由 Bash 内建的指令来执行；</li><li>按 \$PATH 变量指定的搜索路径的顺序找到第一个指令来执行。</li></ol><h2 id="数据流重定向"><a href="#数据流重定向" class="headerlink" title="数据流重定向"></a>数据流重定向</h2><p>重定向指的是使用文件代替标准输入、标准输出和标准错误输出。</p><table><thead><tr><th style="text-align:center">1</th><th style="text-align:center">代码</th><th style="text-align:center">运算符</th></tr></thead><tbody><tr><td style="text-align:center">标准输入 (stdin)</td><td style="text-align:center">0</td><td style="text-align:center">&lt; 或 &lt;&lt;</td></tr><tr><td style="text-align:center">标准输出 (stdout)</td><td style="text-align:center">1</td><td style="text-align:center">&gt; 或 &gt;&gt;</td></tr><tr><td style="text-align:center">标准错误输出 (stderr)</td><td style="text-align:center">2</td><td style="text-align:center">2&gt; 或 2&gt;&gt;</td></tr></tbody></table><p>其中，有一个箭头的表示以覆盖的方式重定向，而有两个箭头的表示以追加的方式重定向。</p><p>可以将不需要的标准输出以及标准错误输出重定向到 /dev/null，相当于扔进垃圾箱。</p><p>如果需要将标准输出以及标准错误输出同时重定向到一个文件，需要将某个输出转换为另一个输出，例如 2&gt;&amp;1 表示将标准错误输出转换为标准输出。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ find /home -name .bashrc &gt; list 2&gt;&amp;1</span><br></pre></td></tr></table></figure><h1 id="七、管线指令"><a href="#七、管线指令" class="headerlink" title="七、管线指令"></a>七、管线指令</h1><p>管线是将一个命令的标准输出作为另一个命令的标准输入，在数据需要经过多个步骤的处理之后才能得到我们想要的内容时就可以使用管线。在命令之间使用 | 分隔各个管线命令。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ ls -al /etc | less</span><br></pre></td></tr></table></figure><h2 id="提取指令"><a href="#提取指令" class="headerlink" title="提取指令"></a>提取指令</h2><p>cut 对数据进行切分，取出想要的部分。提取过程一行一行地进行。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ cut</span><br><span class="line">-d ：分隔符</span><br><span class="line">-f ：经过 -d 分隔后，使用 -f n 取出第 n 个区间</span><br><span class="line">-c ：以字符为单位取出区间</span><br></pre></td></tr></table></figure><p>范例 1：last 将显示的登入者的信息，要求仅显示用户名。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">$ last</span><br><span class="line">root pts/1 192.168.201.101 Sat Feb 7 12:35 still logged in</span><br><span class="line">root pts/1 192.168.201.101 Fri Feb 6 12:13 - 18:46 (06:33)</span><br><span class="line">root pts/1 192.168.201.254 Thu Feb 5 22:37 - 23:53 (01:16)</span><br><span class="line"></span><br><span class="line">$ last | cut -d ' ' -f 1</span><br></pre></td></tr></table></figure><p>范例 2：将 export 输出的讯息，取得第 12 字符以后的所有字符串。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">$ export</span><br><span class="line">declare -x HISTCONTROL="ignoredups"</span><br><span class="line">declare -x HISTSIZE="1000"</span><br><span class="line">declare -x HOME="/home/dmtsai"</span><br><span class="line">declare -x HOSTNAME="study.centos.vbird"</span><br><span class="line">.....(其他省略).....</span><br><span class="line"></span><br><span class="line">$ export | cut -c 12</span><br></pre></td></tr></table></figure><h2 id="排序指令"><a href="#排序指令" class="headerlink" title="排序指令"></a>排序指令</h2><p><strong>sort</strong>  进行排序。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">$ sort [-fbMnrtuk] [file or stdin]</span><br><span class="line">-f ：忽略大小写</span><br><span class="line">-b ：忽略最前面的空格</span><br><span class="line">-M ：以月份的名字来排序，例如 JAN，DEC</span><br><span class="line">-n ：使用数字</span><br><span class="line">-r ：反向排序</span><br><span class="line">-u ：相当于 unique，重复的内容只出现一次</span><br><span class="line">-t ：分隔符，默认为 tab</span><br><span class="line">-k ：指定排序的区间</span><br></pre></td></tr></table></figure><p>范例：/etc/passwd 内容是以 : 来分隔的，以第三栏来排序。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">$ cat /etc/passwd | sort -t ':' -k 3</span><br><span class="line">root:x:0:0:root:/root:/bin/bash</span><br><span class="line">dmtsai:x:1000:1000:dmtsai:/home/dmtsai:/bin/bash</span><br><span class="line">alex:x:1001:1002::/home/alex:/bin/bash</span><br><span class="line">arod:x:1002:1003::/home/arod:/bin/bash</span><br></pre></td></tr></table></figure><p><strong>uniq</strong>  可以将重复的数据只取一个。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ uniq [-ic]</span><br><span class="line">-i ：忽略大小写</span><br><span class="line">-c ：进行计数</span><br></pre></td></tr></table></figure><p>范例：取得每个人的登录总次数</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">$ last | cut -d ' ' -f 1 | sort | uniq -c</span><br><span class="line">1</span><br><span class="line">6 (unknown</span><br><span class="line">47 dmtsai</span><br><span class="line">4 reboot</span><br><span class="line">7 root</span><br><span class="line">1 wtmp</span><br></pre></td></tr></table></figure><h2 id="双向输出重定向"><a href="#双向输出重定向" class="headerlink" title="双向输出重定向"></a>双向输出重定向</h2><p>输出重定向会将输出内容重定向到文件中，而  <strong>tee</strong>  不仅能够完成这个功能，还能保留屏幕上的输出。也就是说，使用 tee 指令，一个输出会同时传送到文件和屏幕上。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ tee [-a] file</span><br></pre></td></tr></table></figure><h2 id="字符转换指令"><a href="#字符转换指令" class="headerlink" title="字符转换指令"></a>字符转换指令</h2><p><strong>tr</strong>  用来删除一行中的字符，或者对字符进行替换。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ tr [-ds] SET1 ...</span><br><span class="line">-d ： 删除行中 SET1 这个字符串</span><br></pre></td></tr></table></figure><p>范例，将 last 输出的信息所有小写转换为大写。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ last | tr '[a-z]' '[A-Z]'</span><br></pre></td></tr></table></figure><p>  <strong>col</strong>  将 tab 字符转为空格字符。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ col [-xb]</span><br><span class="line">-x ： 将 tab 键转换成对等的空格键</span><br></pre></td></tr></table></figure><p><strong>expand</strong>  将 tab 转换一定数量的空格，默认是 8 个。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ expand [-t] file</span><br><span class="line">-t ：tab 转为空格的数量</span><br></pre></td></tr></table></figure><p><strong>join</strong>  将有相同数据的那一行合并在一起。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">$ join [-ti12] file1 file2</span><br><span class="line">-t ：分隔符，默认为空格</span><br><span class="line">-i ：忽略大小写的差异</span><br><span class="line">-1 ：第一个文件所用的比较字段</span><br><span class="line">-2 ：第二个文件所用的比较字段</span><br></pre></td></tr></table></figure><p><strong>paste</strong>  直接将两行粘贴在一起。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ paste [-d] file1 file2</span><br><span class="line">-d ：分隔符，默认为 tab</span><br></pre></td></tr></table></figure><h2 id="分区指令"><a href="#分区指令" class="headerlink" title="分区指令"></a>分区指令</h2><p><strong>split</strong>  将一个文件划分成多个文件。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ split [-bl] file PREFIX</span><br><span class="line">-b ：以大小来进行分区，可加单位，例如 b, k, m 等</span><br><span class="line">-l ：以行数来进行分区。</span><br><span class="line">- PREFIX ：分区文件的前导名称</span><br></pre></td></tr></table></figure><h1 id="八、正则表达式"><a href="#八、正则表达式" class="headerlink" title="八、正则表达式"></a>八、正则表达式</h1><h2 id="grep"><a href="#grep" class="headerlink" title="grep"></a>grep</h2><p>使用正则表示式把匹配的行提取出来。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">$ grep [-acinv] [--color=auto] 搜寻字符串 filename</span><br><span class="line">-a ： 将 binary 文件以 text 文件的方式进行搜寻</span><br><span class="line">-c ： 计算找到个数</span><br><span class="line">-i ： 忽略大小写</span><br><span class="line">-n ： 输出行号</span><br><span class="line">-v ： 反向选择，亦即显示出没有 搜寻字符串 内容的那一行</span><br><span class="line">--color=auto ：找到的关键字加颜色显示</span><br></pre></td></tr></table></figure><p>范例：把含有 the 字符串的行提取出来（注意默认会有 –color=auto 选项，因此以下内容在 Linux 中有颜色显示 the 字符串）</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">$ grep -n 'the' regular_express.txt</span><br><span class="line">8:I can't finish the test.</span><br><span class="line">12:the symbol '*' is represented as start.</span><br><span class="line">15:You are the best is mean you are the no. 1.</span><br><span class="line">16:The world Happy is the same with "glad".</span><br><span class="line">18:google is the best tools for search keyword</span><br></pre></td></tr></table></figure><p>因为 { 和 } 在 shell 是有特殊意义的，因此必须要使用转义字符进行转义。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ grep -n 'go\&#123;2,5\&#125;g' regular_express.txt</span><br></pre></td></tr></table></figure><h2 id="printf"><a href="#printf" class="headerlink" title="printf"></a>printf</h2><p>用于格式化输出。</p><p>它不属于管道命令，在给 printf 传数据时需要使用 $( ) 形式。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ printf '%10s %5i %5i %5i %8.2f \n' $(cat printf.txt)</span><br><span class="line">    DmTsai    80    60    92    77.33</span><br><span class="line">     VBird    75    55    80    70.00</span><br><span class="line">       Ken    60    90    70    73.33</span><br></pre></td></tr></table></figure><h2 id="awk"><a href="#awk" class="headerlink" title="awk"></a>awk</h2><p>可以根据字段的某些条件进行匹配，例如匹配字段小于某个值的那一行数据。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ awk ' 条件类型 1 &#123;动作 1&#125; 条件类型 2 &#123;动作 2&#125; ...' filename</span><br></pre></td></tr></table></figure><p>awk 每次处理一行，处理的最小单位是字段，每个字段的命名方式为：\$n，n 为字段号，从 1 开始，\$0 表示一整行。</p><p>范例 1：取出登录用户的用户名和 ip</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">$ last -n 5</span><br><span class="line">dmtsai pts/0 192.168.1.100 Tue Jul 14 17:32 still logged in</span><br><span class="line">dmtsai pts/0 192.168.1.100 Thu Jul 9 23:36 - 02:58 (03:22)</span><br><span class="line">dmtsai pts/0 192.168.1.100 Thu Jul 9 17:23 - 23:36 (06:12)</span><br><span class="line">dmtsai pts/0 192.168.1.100 Thu Jul 9 08:02 - 08:17 (00:14)</span><br><span class="line">dmtsai tty1 Fri May 29 11:55 - 12:11 (00:15)</span><br><span class="line"></span><br><span class="line">$ last -n 5 | awk '&#123;print $1 "\t" $3&#125;</span><br></pre></td></tr></table></figure><p>awk 变量：</p><table><thead><tr><th>变量名称</th><th>代表意义</th></tr></thead><tbody><tr><td>NF</td><td>每一行拥有的字段总数</td></tr><tr><td>NR</td><td>目前所处理的是第几行数据</td></tr><tr><td>FS</td><td>目前的分隔字符，默认是空格键</td></tr></tbody></table><p>范例 2：输出正在处理的行号，并显示每一行有多少字段</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">$ last -n 5 | awk '&#123;print $1 "\t lines: " NR "\t columns: " NF&#125;'</span><br><span class="line">dmtsai lines: 1 columns: 10</span><br><span class="line">dmtsai lines: 2 columns: 10</span><br><span class="line">dmtsai lines: 3 columns: 10</span><br><span class="line">dmtsai lines: 4 columns: 10</span><br><span class="line">dmtsai lines: 5 columns: 9</span><br></pre></td></tr></table></figure><p>可以使用条件，其中等于使用 ==。</p><p>范例 3：/etc/passwd 文件第三个字段为 UID，对 UID 小于 10 的数据进行处理。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ cat /etc/passwd | awk &apos;BEGIN &#123;FS=&quot;:&quot;&#125; $3 &lt; 10 &#123;print $1 &quot;\t &quot; $3&#125;&apos;</span><br><span class="line">root 0</span><br><span class="line">bin 1</span><br><span class="line">daemon 2</span><br></pre></td></tr></table></figure><h1 id="九、进程管理"><a href="#九、进程管理" class="headerlink" title="九、进程管理"></a>九、进程管理</h1><h2 id="查看进程"><a href="#查看进程" class="headerlink" title="查看进程"></a>查看进程</h2><h3 id="1-ps"><a href="#1-ps" class="headerlink" title="1. ps"></a>1. ps</h3><p>查看某个时间点的进程信息</p><p>示例一：查看自己的进程</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"># ps -l</span><br></pre></td></tr></table></figure><p>示例二：查看系统所有进程</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"># ps aux</span><br></pre></td></tr></table></figure><p>示例三：查看特定的进程</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"># ps aux | grep threadx</span><br></pre></td></tr></table></figure><h3 id="2-top"><a href="#2-top" class="headerlink" title="2. top"></a>2. top</h3><p>实时显示进程信息</p><p>示例：两秒钟刷新一次</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"># top -d 2</span><br></pre></td></tr></table></figure><h3 id="3-pstree"><a href="#3-pstree" class="headerlink" title="3. pstree"></a>3. pstree</h3><p>查看进程树</p><p>示例：查看所有进程树</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"># pstree -A</span><br></pre></td></tr></table></figure><h3 id="4-netstat"><a href="#4-netstat" class="headerlink" title="4. netstat"></a>4. netstat</h3><p>查看占用端口的进程</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"># netstat -anp | grep port</span><br></pre></td></tr></table></figure><h2 id="进程状态"><a href="#进程状态" class="headerlink" title="进程状态"></a>进程状态</h2><p><div align="center"> <img src="https://poo1.club/img/76a49594323247f21c9b3a69945445ee.png" width=""> </div><br></p><table><thead><tr><th style="text-align:center">状态</th><th>说明</th></tr></thead><tbody><tr><td style="text-align:center">R</td><td>running or runnable (on run queue)</td></tr><tr><td style="text-align:center">D</td><td>uninterruptible sleep (usually IO)</td></tr><tr><td style="text-align:center">S</td><td>interruptible sleep (waiting for an event to complete)</td></tr><tr><td style="text-align:center">Z</td><td>defunct/zombie, terminated but not reaped by its parent</td></tr><tr><td style="text-align:center">T</td><td>stopped, either by a job control signal or because it is being traced</td></tr></tbody></table><h2 id="SIGCHILD"><a href="#SIGCHILD" class="headerlink" title="SIGCHILD"></a>SIGCHILD</h2><p>当一个子进程改变了它的状态时：停止运行，继续运行或者退出，有两件事会发生在父进程中：</p><ul><li>得到 SIGCHLD 信号；</li><li>阻塞的 waitpid(2)（或者 wait）调用会返回。</li></ul><p><div align="center"> <img src="https://poo1.club/img/flow.png" width=""> </div><br></p><h2 id="孤儿进程和僵死进程"><a href="#孤儿进程和僵死进程" class="headerlink" title="孤儿进程和僵死进程"></a>孤儿进程和僵死进程</h2><h3 id="1-孤儿进程"><a href="#1-孤儿进程" class="headerlink" title="1. 孤儿进程"></a>1. 孤儿进程</h3><p>一个父进程退出，而它的一个或多个子进程还在运行，那么这些子进程将成为孤儿进程。孤儿进程将被 init 进程（进程号为 1）所收养，并由 init 进程对它们完成状态收集工作。</p><p>由于孤儿进程会被 init 进程收养，所以孤儿进程不会对系统造成危害。</p><h3 id="2-僵死进程"><a href="#2-僵死进程" class="headerlink" title="2. 僵死进程"></a>2. 僵死进程</h3><p>一个子进程的进程描述符在子进程退出时不会释放，只有当父进程通过 wait 或 waitpid 获取了子进程信息后才会释放。如果子进程退出，而父进程并没有调用 wait 或 waitpid，那么子进程的进程描述符仍然保存在系统中，这种进程称之为僵死进程。</p><p>僵死进程通过 ps 命令显示出来的状态为 Z。</p><p>系统所能使用的进程号是有限的，如果大量的产生僵死进程，将因为没有可用的进程号而导致系统不能产生新的进程。</p><p>要消灭系统中大量的僵死进程，只需要将其父进程杀死，此时所有的僵死进程就会变成孤儿进程，从而被 init 所收养，这样 init 就会释放所有的僵死进程所占有的资源，从而结束僵死进程。</p><h1 id="十、I-O-复用"><a href="#十、I-O-复用" class="headerlink" title="十、I/O 复用"></a>十、I/O 复用</h1><h2 id="概念理解"><a href="#概念理解" class="headerlink" title="概念理解"></a>概念理解</h2><p>I/O Multiplexing 又被称为 Event Driven I/O，它可以让单个进程具有处理多个 I/O 事件的能力。</p><p>当某个 I/O 事件条件满足时，进程会收到通知。</p><p>如果一个 Web 服务器没有 I/O 复用，那么每一个 Socket 连接都需要创建一个线程去处理。如果同时连接几万个连接，那么就需要创建相同数量的线程。并且相比于多进程和多线程技术，I/O 复用不需要进程线程创建和切换的开销，系统开销更小。</p><h2 id="I-O-模型"><a href="#I-O-模型" class="headerlink" title="I/O 模型"></a>I/O 模型</h2><ul><li>阻塞（Blocking）</li><li>非阻塞（Non-blocking）</li><li>同步（Synchronous）</li><li>异步（Asynchronous）</li></ul><p>阻塞非阻塞是等待 I/O 完成的方式，阻塞要求用户程序停止执行，直到 I/O 完成，而非阻塞在 I/O 完成之前还可以继续执行。</p><p>同步异步是获知 I/O 完成的方式，同步需要时刻关心 I/O 是否已经完成，异步无需主动关心，在 I/O 完成时它会收到通知。</p><p><div align="center"> <img src="https://poo1.club/img/54cb3f21-485b-4159-8bf5-dcde1c4d4c36.png" width=""> </div><br></p><h3 id="1-同步-阻塞"><a href="#1-同步-阻塞" class="headerlink" title="1. 同步-阻塞"></a>1. 同步-阻塞</h3><p>这是最常见的一种模型，用户程序在使用 read() 时会执行系统调用从而陷入内核，之后就被阻塞直到系统调用完成。</p><p>应该注意到，在阻塞的过程中，其他程序还可以执行，因此阻塞不意味着整个操作系统都被阻塞。因为其他程序还可以执行，因此不消耗 CPU 时间，这种模型的执行效率会比较高。</p><p><div align="center"> <img src="https://poo1.club/img/5e9b10f3-9504-4483-9667-d4770adebf9f.png" width=""> </div><br></p><h3 id="2-同步-非阻塞"><a href="#2-同步-非阻塞" class="headerlink" title="2. 同步-非阻塞"></a>2. 同步-非阻塞</h3><p>非阻塞意味着用户程序在执行系统调用后还可以继续执行，内核并不是马上执行完 I/O，而是以一个错误码来告知用户程序 I/O 还未完成。为了获得 I/O 完成事件，用户程序必须调用多次系统调用去询问内核，甚至是忙等，也就是在一个循环里面一直询问并等待。</p><p>由于 CPU 要处理更多的用户程序的询问，因此这种模型的效率是比较低的。</p><p><div align="center"> <img src="https://poo1.club/img/1582217a-ed46-4cac-811e-90d13a65163b.png" width=""> </div><br></p><h3 id="3-异步-阻塞"><a href="#3-异步-阻塞" class="headerlink" title="3. 异步-阻塞"></a>3. 异步-阻塞</h3><p>这是 I/O 复用使用的一种模式，通过使用 select，它可以监听多个 I/O 事件，当这些事件至少有一个发生时，用户程序会收到通知。</p><p><div align="center"> <img src="https://poo1.club/img/dbc5c9f1-c13c-4d06-86ba-7cc949eb4c8f.jpg" width=""> </div><br></p><h3 id="4-异步-非阻塞"><a href="#4-异步-非阻塞" class="headerlink" title="4. 异步-非阻塞"></a>4. 异步-非阻塞</h3><p>该模式下，I/O 操作会立即返回，之后可以处理其它操作，并且在 I/O 完成时会收到一个通知，此时会中断正在处理的操作，然后继续之前的操作。</p><p><div align="center"> <img src="https://poo1.club/img/b4b29aa9-dd2c-467b-b75f-ca6541cb25b5.jpg" width=""> </div><br></p><h2 id="select-poll-epoll"><a href="#select-poll-epoll" class="headerlink" title="select poll epoll"></a>select poll epoll</h2><p>这三个都是 I/O 多路复用的具体实现，select 出现的最早，之后是 poll，再是 epoll。</p><h3 id="1-select"><a href="#1-select" class="headerlink" title="1. select"></a>1. select</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">select</span> <span class="params">(<span class="keyword">int</span> n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)</span></span>;</span><br></pre></td></tr></table></figure><ul><li>fd_set 表示描述符集合；</li><li>readset、writeset 和 exceptset 这三个参数指定让操作系统内核测试读、写和异常条件的描述符；</li><li>timeout 参数告知内核等待所指定描述符中的任何一个就绪可花多少时间；</li><li>成功调用返回结果大于 0；出错返回结果为 -1；超时返回结果为 0。</li></ul><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line">fd_set fd_in, fd_out;</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">timeval</span> <span class="title">tv</span>;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Reset the sets</span></span><br><span class="line">FD_ZERO( &amp;fd_in );</span><br><span class="line">FD_ZERO( &amp;fd_out );</span><br><span class="line"></span><br><span class="line"><span class="comment">// Monitor sock1 for input events</span></span><br><span class="line">FD_SET( sock1, &amp;fd_in );</span><br><span class="line"></span><br><span class="line"><span class="comment">// Monitor sock2 for output events</span></span><br><span class="line">FD_SET( sock2, &amp;fd_out );</span><br><span class="line"></span><br><span class="line"><span class="comment">// Find out which socket has the largest numeric value as select requires it</span></span><br><span class="line"><span class="keyword">int</span> largest_sock = sock1 &gt; sock2 ? sock1 : sock2;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Wait up to 10 seconds</span></span><br><span class="line">tv.tv_sec = <span class="number">10</span>;</span><br><span class="line">tv.tv_usec = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Call the select</span></span><br><span class="line"><span class="keyword">int</span> ret = select( largest_sock + <span class="number">1</span>, &amp;fd_in, &amp;fd_out, <span class="literal">NULL</span>, &amp;tv );</span><br><span class="line"></span><br><span class="line"><span class="comment">// Check if select actually succeed</span></span><br><span class="line"><span class="keyword">if</span> ( ret == <span class="number">-1</span> )</span><br><span class="line">    <span class="comment">// report error and abort</span></span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span> ( ret == <span class="number">0</span> )</span><br><span class="line">    <span class="comment">// timeout; no event detected</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span> ( FD_ISSET( sock1, &amp;fd_in ) )</span><br><span class="line">        <span class="comment">// input event on sock1</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> ( FD_ISSET( sock2, &amp;fd_out ) )</span><br><span class="line">        <span class="comment">// output event on sock2</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>每次调用 select() 都需要将 fd_set *readfds, fd_set *writefds, fd_set *exceptfds 链表内容全部从用户进程内存中复制到操作系统内核中，内核需要将所有 fd_set 遍历一遍，这个过程非常低效。</p><p>返回结果中内核并没有声明哪些 fd_set 已经准备好了，所以如果返回值大于 0 时，程序需要遍历所有的 fd_set 判断哪个 I/O 已经准备好。</p><p>在 Linux 中 select 最多支持 1024 个 fd_set 同时轮询，其中 1024 由 Linux 内核的 FD_SETSIZE 决定。如果需要打破该限制可以修改 FD_SETSIZE，然后重新编译内核。</p><h3 id="2-poll"><a href="#2-poll" class="headerlink" title="2. poll"></a>2. poll</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">poll</span> <span class="params">(struct pollfd *fds, <span class="keyword">unsigned</span> <span class="keyword">int</span> nfds, <span class="keyword">int</span> timeout)</span></span>;</span><br></pre></td></tr></table></figure><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">pollfd</span> &#123;</span></span><br><span class="line">    <span class="keyword">int</span> fd;       <span class="comment">//文件描述符</span></span><br><span class="line">    <span class="keyword">short</span> events; <span class="comment">//监视的请求事件</span></span><br><span class="line">    <span class="keyword">short</span> revents; <span class="comment">//已发生的事件</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// The structure for two events</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">pollfd</span> <span class="title">fds</span>[2];</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Monitor sock1 for input</span></span><br><span class="line">fds[<span class="number">0</span>].fd = sock1;</span><br><span class="line">fds[<span class="number">0</span>].events = POLLIN;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Monitor sock2 for output</span></span><br><span class="line">fds[<span class="number">1</span>].fd = sock2;</span><br><span class="line">fds[<span class="number">1</span>].events = POLLOUT;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Wait 10 seconds</span></span><br><span class="line"><span class="keyword">int</span> ret = poll( &amp;fds, <span class="number">2</span>, <span class="number">10000</span> );</span><br><span class="line"><span class="comment">// Check if poll actually succeed</span></span><br><span class="line"><span class="keyword">if</span> ( ret == <span class="number">-1</span> )</span><br><span class="line">    <span class="comment">// report error and abort</span></span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span> ( ret == <span class="number">0</span> )</span><br><span class="line">    <span class="comment">// timeout; no event detected</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">// If we detect the event, zero it out so we can reuse the structure</span></span><br><span class="line">    <span class="keyword">if</span> ( pfd[<span class="number">0</span>].revents &amp; POLLIN )</span><br><span class="line">        pfd[<span class="number">0</span>].revents = <span class="number">0</span>;</span><br><span class="line">        <span class="comment">// input event on sock1</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> ( pfd[<span class="number">1</span>].revents &amp; POLLOUT )</span><br><span class="line">        pfd[<span class="number">1</span>].revents = <span class="number">0</span>;</span><br><span class="line">        <span class="comment">// output event on sock2</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>它和 select() 功能基本相同。同样需要每次将 struct pollfd *fds 复制到内核，返回后同样需要进行轮询每一个 pollfd 是否已经 I/O 准备好。poll() 取消了 1024 个描述符数量上限，但是数量太大以后不能保证执行效率，因为复制大量内存到内核十分低效，所需时间与描述符数量成正比。poll() 在 pollfd 的重复利用上比 select() 的 fd_set 会更好。</p><p>如果在多线程下，如果一个线程对某个描述符调用了 poll() 系统调用，但是另一个线程关闭了该描述符，会导致 poll() 调用结果不确定，该问题同样出现在 select() 中。</p><h3 id="3-epoll"><a href="#3-epoll" class="headerlink" title="3. epoll"></a>3. epoll</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">epoll_create</span><span class="params">(<span class="keyword">int</span> size)</span></span>;</span><br><span class="line">int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)；</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">epoll_wait</span><span class="params">(<span class="keyword">int</span> epfd, struct epoll_event * events, <span class="keyword">int</span> maxevents, <span class="keyword">int</span> timeout)</span></span>;</span><br></pre></td></tr></table></figure><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Create the epoll descriptor. Only one is needed per app, and is used to monitor all sockets.</span></span><br><span class="line"><span class="comment">// The function argument is ignored (it was not before, but now it is), so put your favorite number here</span></span><br><span class="line"><span class="keyword">int</span> pollingfd = epoll_create( <span class="number">0xCAFE</span> );</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> ( pollingfd &lt; <span class="number">0</span> )</span><br><span class="line"> <span class="comment">// report error</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Initialize the epoll structure in case more members are added in future</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">epoll_event</span> <span class="title">ev</span> = &#123;</span> <span class="number">0</span> &#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Associate the connection class instance with the event. You can associate anything</span></span><br><span class="line"><span class="comment">// you want, epoll does not use this information. We store a connection class pointer, pConnection1</span></span><br><span class="line">ev.data.ptr = pConnection1;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Monitor for input, and do not automatically rearm the descriptor after the event</span></span><br><span class="line">ev.events = EPOLLIN | EPOLLONESHOT;</span><br><span class="line"><span class="comment">// Add the descriptor into the monitoring list. We can do it even if another thread is</span></span><br><span class="line"><span class="comment">// waiting in epoll_wait - the descriptor will be properly added</span></span><br><span class="line"><span class="keyword">if</span> ( epoll_ctl( epollfd, EPOLL_CTL_ADD, pConnection1-&gt;getSocket(), &amp;ev ) != <span class="number">0</span> )</span><br><span class="line">    <span class="comment">// report error</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Wait for up to 20 events (assuming we have added maybe 200 sockets before that it may happen)</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">epoll_event</span> <span class="title">pevents</span>[ 20 ];</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Wait for 10 seconds, and retrieve less than 20 epoll_event and store them into epoll_event array</span></span><br><span class="line"><span class="keyword">int</span> ready = epoll_wait( pollingfd, pevents, <span class="number">20</span>, <span class="number">10000</span> );</span><br><span class="line"><span class="comment">// Check if epoll actually succeed</span></span><br><span class="line"><span class="keyword">if</span> ( ret == <span class="number">-1</span> )</span><br><span class="line">    <span class="comment">// report error and abort</span></span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span> ( ret == <span class="number">0</span> )</span><br><span class="line">    <span class="comment">// timeout; no event detected</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">// Check if any events detected</span></span><br><span class="line">    <span class="keyword">for</span> ( <span class="keyword">int</span> i = <span class="number">0</span>; i &lt; ret; i++ )</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span> ( pevents[i].events &amp; EPOLLIN )</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="comment">// Get back our connection pointer</span></span><br><span class="line">            Connection * c = (Connection*) pevents[i].data.ptr;</span><br><span class="line">            c-&gt;handleReadEvent();</span><br><span class="line">         &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>epoll 仅仅适用于 Linux OS。</p><p>它是 select 和 poll 的增强版，更加灵活而且没有描述符限制。它将用户关心的描述符放到内核的一个事件表中，从而只需要在用户空间和内核空间拷贝一次。</p><p>select 和 poll 方式中，进程只有在调用一定的方法后，内核才对所有监视的描述符进行扫描。而 epoll 事先通过 epoll_ctl() 来注册描述符，一旦基于某个描述符就绪时，内核会采用类似 callback 的回调机制，迅速激活这个描述符，当进程调用 epoll_wait() 时便得到通知。</p><p>新版本的 epoll_create(int size) 参数 size 不起任何作用，在旧版本的 epoll 中如果描述符的数量大于 size，不保证服务质量。</p><p>epoll_ctl() 执行一次系统调用，用于向内核注册新的描述符或者是改变某个文件描述符的状态。已注册的描述符在内核中会被维护在一棵红黑树上，通过回调函数内核会将 I/O 准备好的描述符加入到一个链表中管理。</p><p>epoll_wait() 取出在内核中通过链表维护的 I/O 准备好的描述符，将他们从内核复制到程序中，不需要像 select/poll 对注册的所有描述符遍历一遍。</p><p>epoll 对多线程编程更有友好，同时多个线程对同一个描述符调用了 epoll_wait 也不会产生像 select/poll 的不确定情况。或者一个线程调用了 epoll_wait 另一个线程关闭了同一个描述符也不会产生不确定情况。</p><h2 id="select-和-poll-比较"><a href="#select-和-poll-比较" class="headerlink" title="select 和 poll 比较"></a>select 和 poll 比较</h2><h3 id="1-功能"><a href="#1-功能" class="headerlink" title="1. 功能"></a>1. 功能</h3><p>它们提供了几乎相同的功能，但是在一些细节上有所不同：</p><ul><li>select 会修改 fd_set 参数，而 poll 不会；</li><li>select 默认只能监听 1024 个描述符，如果要监听更多的话，需要修改 FD_SETSIZE 之后重新编译；</li><li>poll 提供了更多的事件类型。</li></ul><h3 id="2-速度"><a href="#2-速度" class="headerlink" title="2. 速度"></a>2. 速度</h3><p>poll 和 select 在速度上都很慢。</p><ul><li>它们都采取轮询的方式来找到 I/O 完成的描述符，如果描述符很多，那么速度就会很慢；</li><li>select 只使用每个描述符的 3 位，而 poll 通常需要使用 64 位，因此 poll 需要复制更多的内核空间。</li></ul><h3 id="3-可移植性"><a href="#3-可移植性" class="headerlink" title="3. 可移植性"></a>3. 可移植性</h3><p>几乎所有的系统都支持 select，但是只有比较新的系统支持 poll。</p><h2 id="eopll-工作模式"><a href="#eopll-工作模式" class="headerlink" title="eopll 工作模式"></a>eopll 工作模式</h2><p>epoll_event 有两种触发模式：LT（level trigger）和 ET（edge trigger）。</p><h3 id="1-LT-模式"><a href="#1-LT-模式" class="headerlink" title="1. LT 模式"></a>1. LT 模式</h3><p>当 epoll_wait() 检测到描述符事件发生并将此事件通知应用程序，应用程序可以不立即处理该事件。下次调用 epoll_wait() 时，会再次响应应用程序并通知此事件。是默认的一种模式，并且同时支持 Blocking 和 No-Blocking。</p><h3 id="2-ET-模式"><a href="#2-ET-模式" class="headerlink" title="2. ET 模式"></a>2. ET 模式</h3><p>当 epoll_wait() 检测到描述符事件发生并将此事件通知应用程序，应用程序必须立即处理该事件。如果不处理，下次调用 epoll_wait() 时，不会再次响应应用程序并通知此事件。很大程度上减少了 epoll 事件被重复触发的次数，因此效率要比 LT 模式高。只支持 No-Blocking，以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。</p><h2 id="select-poll-epoll-应用场景"><a href="#select-poll-epoll-应用场景" class="headerlink" title="select poll epoll 应用场景"></a>select poll epoll 应用场景</h2><p>很容易产生一种错觉认为只要用 epoll 就可以了，select poll 都是历史遗留问题，并没有什么应用场景，其实并不是这样的。</p><h3 id="1-select-应用场景"><a href="#1-select-应用场景" class="headerlink" title="1. select 应用场景"></a>1. select 应用场景</h3><p>select() poll() epoll_wait() 都有一个 timeout 参数，在 select() 中 timeout 的精确度为 1ns，而 poll() 和 epoll_wait() 中则为 1ms。所以 select 更加适用于实时要求更高的场景，比如核反应堆的控制。</p><p>select 历史更加悠久，它的可移植性更好，几乎被所有主流平台所支持。</p><h3 id="2-poll-应用场景"><a href="#2-poll-应用场景" class="headerlink" title="2. poll 应用场景"></a>2. poll 应用场景</h3><p>poll 没有最大描述符数量的限制，如果平台支持应该采用 poll 且对实时性要求并不是十分严格，而不是 select。</p><p>需要同时监控小于 1000 个描述符。那么也没有必要使用 epoll，因为这个应用场景下并不能体现 epoll 的优势。</p><p>需要监控的描述符状态变化多，而且都是非常短暂的。因为 epoll 中的所有描述符都存储在内核中，造成每次需要对描述符的状态改变都需要通过 epoll_ctl() 进行系统调用，频繁系统调用降低效率。epoll 的描述符存储在内核，不容易调试。</p><h3 id="3-epoll-应用场景"><a href="#3-epoll-应用场景" class="headerlink" title="3. epoll 应用场景"></a>3. epoll 应用场景</h3><p>程序只需要运行在 Linux 平台上，有非常大量的描述符需要同时轮询，而且这些连接最好是长连接。</p><h3 id="4-性能对比"><a href="#4-性能对比" class="headerlink" title="4. 性能对比"></a>4. 性能对比</h3><blockquote><p><a href="http://lse.sourceforge.net/epoll/index.html" target="_blank" rel="noopener">epoll Scalability Web Page</a></p></blockquote><h1 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h1><ul><li>鸟哥. 鸟 哥 的 Linux 私 房 菜 基 础 篇 第 三 版[J]. 2009.</li><li><a href="https://www.ibm.com/developerworks/cn/linux/l-cn-rpmdpkg/index.html" target="_blank" rel="noopener">Linux 平台上的软件包管理</a></li><li><a href="https://www.ibm.com/developerworks/linux/library/l-async/" target="_blank" rel="noopener">Boost application performance using asynchronous I/O</a></li><li><a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa365683(v=vs.85" target="_blank" rel="noopener">Synchronous and Asynchronous I/O</a>.aspx)</li><li><a href="https://segmentfault.com/a/1190000003063859" target="_blank" rel="noopener">Linux IO 模式及 select、poll、epoll 详解</a></li><li><a href="https://daniel.haxx.se/docs/poll-vs-select.html" target="_blank" rel="noopener">poll vs select vs event-based</a></li><li><a href="http://liubigbin.github.io/2016/03/11/Linux-%E4%B9%8B%E5%AE%88%E6%8A%A4%E8%BF%9B%E7%A8%8B%E3%80%81%E5%83%B5%E6%AD%BB%E8%BF%9B%E7%A8%8B%E4%B8%8E%E5%AD%A4%E5%84%BF%E8%BF%9B%E7%A8%8B/" target="_blank" rel="noopener">Linux 之守护进程、僵死进程与孤儿进程</a></li><li><a href="https://idea.popcount.org/2012-12-11-linux-process-states/" target="_blank" rel="noopener">Linux process states</a></li><li><a href="https://en.wikipedia.org/wiki/GUID_Partition_Table" target="_blank" rel="noopener">GUID Partition Table</a></li></ul>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;一、常用操作以及概念&quot;&gt;&lt;a href=&quot;#一、常用操作以及概念&quot; class=&quot;headerlink&quot; title=&quot;一、常用操作以及概念&quot;&gt;&lt;/a&gt;一、常用操作以及概念&lt;/h1&gt;&lt;h2 id=&quot;求助&quot;&gt;&lt;a href=&quot;#求助&quot; class=&quot;headerli
      
    
    </summary>
    
    
      <category term="Linux" scheme="https://www.07sec.com/tags/Linux/"/>
    
      <category term="Shell" scheme="https://www.07sec.com/tags/Shell/"/>
    
  </entry>
  
  <entry>
    <title>提问的智慧</title>
    <link href="https://www.07sec.com/2018/04/03/How-To-Ask-Questions-The-Smart-Way/"/>
    <id>https://www.07sec.com/2018/04/03/How-To-Ask-Questions-The-Smart-Way/</id>
    <published>2022-04-03T03:23:04.000Z</published>
    <updated>2022-04-03T03:27:58.000Z</updated>
    
    <content type="html"><![CDATA[<p>#提问的智慧<br><a id="more"></a><br><strong>How To Ask Questions The Smart Way</strong></p><p>Copyright © 2001,2006,2014 Eric S. Raymond, Rick Moen</p><p>本指南英文版版权为 Eric S. Raymond, Rick Moen 所有。</p><p>原文网址:<a href="http://www.catb.org/~esr/faqs/smart-questions.html" target="_blank" rel="noopener">http://www.catb.org/~esr/faqs/smart-questions.html</a></p><p>Copyleft 2001 by D.H.Grand(nOBODY/Ginux), 2010 by Gasolin, 2015 by Ryan Wu</p><p>本中文指南是基于原文 3.10 版以及 2010 年由 <a href="https://github.com/gasolin" target="_blank" rel="noopener">Gasolin</a> 所翻译版本的最新翻译；</p><p>协助指出翻译问题，<strong>请<a href="https://github.com/ryanhanwu/smartquestions/issues/new" target="_blank" rel="noopener">发Issue</a>，或直接<a href="https://github.com/ryanhanwu/smartquestions/compare/" target="_blank" rel="noopener">发Pull Request</a>给我。</strong></p><p>本文另有繁体中文版: <a href="https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way" target="_blank" rel="noopener">https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way</a></p><h2 id="原文版本历史"><a href="#原文版本历史" class="headerlink" title="原文版本历史"></a><a href="https://github.com/ryanhanwu/smartquestions/blob/master/history.md" target="_blank" rel="noopener">原文版本历史</a></h2><p>##声明</p><p>许多项目在他们的使用协助/说明网页中链接了本指南，这么做很好，我们也鼓励大家都这么做。但如果你是负责管理这个项目网页的人，请在超链接附近的显着位置上注明：</p><p><strong>本指南不提供此项目的实际支持服务！</strong></p><p>我们已经深刻领教到少了上述声明所带来的痛苦。因为少了这点声明，我们不停地被一些白痴纠缠。这些白痴认为既然我们发布了这本指南，那么我们就有责任解决世上所有的技术问题。</p><p>如果你是因为需要某些协助而正在阅读这本指南，并且最后离开是因为发现从本指南作者们身上得不到直接的协助，那么你就是我们所说的那些白痴之一。别问我们问题，我们只会忽略你。我们在这本指南中是教你如何从那些真正懂得你所遇到软件或硬件问题的人取得协助，而99%的情况下那不会是我们。除非你确定本指南的作者之一刚好是你所遇到的问题领域的专家，否则请不要打扰我们，这样大家都会开心一点。</p><p>##简介</p><p>在<a href="http://www.catb.org/~esr/faqs/hacker-howto.html" target="_blank" rel="noopener">黑客</a>的世界里，当你拋出一个技术问题时，最终是否能得到有用的回答，往往取决于你所提问和追问的方式。本指南将教你如何正确的提问以获得你满意的答案。</p><p>不只是黑客，现在开放源代码（Open Source）软件已经相当盛行，你常常也可以由其他有经验的使用者身上得到好答案，这是件<strong>_好事_</strong>；使用者比起黑客来，往往对那些新手常遇到的问题更宽容一些。然而，将有经验的使用者视为黑客，并采用本指南所提的方法与他们沟通，同样也是能从他们身上得到满意回答的最有效方式。</p><p>首先你应该明白，黑客们喜爱有挑战性的问题，或者能激发我们思维的好问题。如果我们并非如此，那我们也不会成为你想询问的对象。如果你给了我们一个值得反复咀嚼玩味的好问题，我们自会对你感激不尽。好问题是激励，是厚礼。好问题可以提高我们的理解力，而且通常会暴露我们以前从没意识到或者思考过的问题。对黑客而言，”好问题！”是诚挚的大力称赞。</p><p>尽管如此，黑客们有着蔑视或傲慢面对简单问题的坏名声，这有时让我们看起来对新手、无知者似乎较有敌意，但其实不是那样的。</p><p>我们不讳言我们对那些不愿思考、或者在发问前不做他们该做的事的人的蔑视。那些人是时间杀手 -– 他们只想索取，从不付出，消耗我们可用在更有趣的问题或更值得回答的人身上的时间。我们称这样的人为 <figure class="highlight plain"><figcaption><span>（由于历史原因，我们有时把它拼作 ```lusers```）。</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">我们意识到许多人只是想使用我们写的软件，他们对学习技术细节没有兴趣。对大多数人而言，电脑只是种工具，是种达到目的的手段而已。他们有自己的生活并且有更要紧的事要做。我们了解这点，也从不指望每个人都对这些让我们着迷的技术问题感兴趣。尽管如此，我们回答问题的风格是指向那些真正对此有兴趣并愿意主动参与解决问题的人，这一点不会变，也不该变。如果连这都变了，我们就是在降低做自己最擅长的事情上的效率。</span><br><span class="line"></span><br><span class="line">我们（在很大程度上）是自愿的，从繁忙的生活中抽出时间来解答疑惑，而且时常被提问淹没。所以我们无情的滤掉一些话题，特别是拋弃那些看起来像失败者的家伙，以便更高效的利用时间来回答```赢家（winner）```的问题。</span><br><span class="line"></span><br><span class="line">如果你厌恶我们的态度，高高在上，或过于傲慢，不妨也设身处地想想。我们并没有要求你向我们屈服 -- 事实上，我们大多数人非常乐意与你平等地交流，只要你付出小小努力来满足基本要求，我们就会欢迎你加入我们的文化。但让我们帮助那些不愿意帮助自己的人是没有效率的。无知没有关系，但装白痴就是不行。</span><br><span class="line"></span><br><span class="line">所以，你不必在技术上很在行才能吸引我们的注意，但你必须表现出能引导你变得在行的特质 -- 机敏、有想法、善于观察、乐于主动参与解决问题。如果你做不到这些使你与众不同的事情，我们建议你花点钱找家商业公司签个技术支持服务合同，而不是要求黑客个人无偿地帮助你。</span><br><span class="line"></span><br><span class="line">如果你决定向我们求助，当然你也不希望被视为失败者，更不愿成为失败者中的一员。能立刻得到快速并有效答案的最好方法，就是像赢家那样提问 -- 聪明、自信、有解决问题的思路，只是偶尔在特定的问题上需要获得一点帮助。</span><br><span class="line"></span><br><span class="line">（欢迎对本指南提出改进意见。你可以 email 你的建议至 [esr@thyrsus.com](esr@thyrsus.com) 或 [respond-auto@linuxmafia.com](respond-auto@linuxmafia.com)。然而请注意，本文并非[网络礼节](http://www.ietf.org/rfc/rfc1855.txt)的通用指南，而我们通常会拒绝无助于在技术论坛得到有用答案的建议。）</span><br><span class="line"></span><br><span class="line">##在提问之前</span><br><span class="line"></span><br><span class="line">在你准备要通过电子邮件、新闻群组或者聊天室提出技术问题前，请先做到以下事情：</span><br><span class="line"></span><br><span class="line">  1. 尝试在你准备提问的论坛的旧文章中搜索答案。</span><br><span class="line">  1. 尝试上网搜索以找到答案。</span><br><span class="line">  1. 尝试阅读手册以找到答案。</span><br><span class="line">  1. 尝试阅读常见问题文件（FAQ）以找到答案。</span><br><span class="line">  1. 尝试自己检查或试验以找到答案</span><br><span class="line">  1. 向你身边的强者朋友打听以找到答案。</span><br><span class="line">  1. 如果你是程序开发者，请尝试阅读源代码以找到答案</span><br><span class="line"></span><br><span class="line">当你提出问题的时候，请先表明你已经做了上述的努力；这将有助于树立你并不是一个不劳而获且浪费别人的时间的提问者。如果你能一并表达在做了上述努力的过程中所**_学到_**的东西会更好，因为我们更乐于回答那些表现出能从答案中学习的人的问题。</span><br><span class="line"></span><br><span class="line">运用某些策略，比如先用Google搜索你所遇到的各种错误信息（既搜索[Google论坛](http://groups.google.com/)，也搜索网页），这样很可能直接就找到了能解决问题的文件或邮件列表线索。即使没有结果，在邮件列表或新闻组寻求帮助时加上一句 ```我在Google中搜过下列句子但没有找到什么有用的东西``` 也是件好事，即使它只是表明了搜索引擎不能提供哪些帮助。这么做（加上搜索过的字串）也让遇到相似问题的其他人能被搜索引擎引导到你的提问来。</span><br><span class="line"></span><br><span class="line">别着急，不要指望几秒钟的Google搜索就能解决一个复杂的问题。在向专家求助之前，再阅读一下常见问题文件（FAQ）、放轻松、坐舒服一些，再花点时间思考一下这个问题。相信我们，他们能从你的提问看出你做了多少阅读与思考，如果你是有备而来，将更有可能得到解答。不要将所有问题一股脑拋出，只因你的第一次搜索没有找到答案（或者找到太多答案）。</span><br><span class="line"></span><br><span class="line">准备好你的问题，再将问题仔细的思考过一遍，因为草率的发问只能得到草率的回答，或者根本得不到任何答案。越是能表现出在寻求帮助前你为解决问题所付出的努力，你越有可能得到实质性的帮助。</span><br><span class="line"></span><br><span class="line">小心别问错了问题。如果你的问题基于错误的假设，某个普通黑客（J. Random Hacker）多半会一边在心里想着```蠢问题…```， 一边用无意义的字面解释来答复你，希望着你会从问题的回答（而非你想得到的答案）中汲取教训。</span><br><span class="line"></span><br><span class="line">绝不要自以为**_够格_**得到答案，你没有；你并没有。毕竟你没有为这种服务支付任何报酬。你将会是自己去**_挣到_**一个答案，靠提出有内涵的、有趣的、有思维激励作用的问题 --一个有潜力能贡献社区经验的问题，而不仅仅是被动的从他人处索取知识。</span><br><span class="line"></span><br><span class="line">另一方面，表明你愿意在找答案的过程中做点什么是一个非常好的开端。```谁能给点提示？```、```我的这个例子里缺了什么？```以及```我应该检查什么地方```比```请把我需要的确切的过程贴出来```更容易得到答复。因为你表现出只要有人能指个正确方向，你就有完成它的能力和决心。</span><br><span class="line"></span><br><span class="line">##当你提问时</span><br><span class="line"></span><br><span class="line">###慎选提问的论坛</span><br><span class="line"></span><br><span class="line">小心选择你要提问的场合。如果你做了下述的事情，你很可能被忽略掉或者被看作失败者：</span><br><span class="line"></span><br><span class="line">  * 在与主题不合的论坛上贴出你的问题</span><br><span class="line">  * 在探讨进阶技术问题的论坛张贴非常初级的问题；反之亦然</span><br><span class="line">  * 在太多的不同新闻群组上重复转贴同样的问题（cross-post）</span><br><span class="line">  * 向既非熟人也没有义务解决你问题的人发送私人电邮</span><br><span class="line"></span><br><span class="line">黑客会剔除掉那些搞错场合的问题，以保护他们沟通的渠道不被无关的东西淹没。你不会想让这种事发生在自己身上的。</span><br><span class="line"></span><br><span class="line">因此，第一步是找到对的论坛。再说一次，Google和其它搜索引擎还是你的朋友，用它们来找到与你遭遇到困难的软硬件问题最相关的网站。通常那儿都有常见问题（FAQ）、邮件列表及相关说明文件的链接。如果你的努力（包括**_阅读_**FAQ）都没有结果，网站上也许还有报告Bug（Bug-reporting）的流程或链接，如果是这样，连过去看看。</span><br><span class="line"></span><br><span class="line">向陌生的人或论坛发送邮件最可能是风险最大的事情。举例来说，别假设一个提供丰富内容的网页的作者会想充当你的免费顾问。不要对你的问题是否会受到欢迎做太乐观的估计 -- 如果你不确定，那就向别处发送，或者压根别发。</span><br><span class="line"></span><br><span class="line">在选择论坛、新闻群组或邮件列表时，别太相信名字，先看看FAQ或者许可书以弄清楚你的问题是否切题。发文前先翻翻已有的话题，这样可以让你感受一下那里的文化。事实上，事先在新闻组或邮件列表的历史记录中搜索与你问题相关的关键词是个极好的主意，也许这样就找到答案了。即使没有，也能帮助你归纳出更好的问题。</span><br><span class="line"></span><br><span class="line">别像机关枪似的一次&quot;扫射&quot;所有的帮助渠道，这就像大喊大叫一样会使人不快。要一个一个地来。</span><br><span class="line"></span><br><span class="line">搞清楚你的主题！最典型的错误之一是在某种致力于跨平台可移植的语言、套件或工具的论坛中提关于Unix或Windows操作系统程序界面的问题。如果你不明白为什么这是大错，最好在搞清楚这之间差异之前什么也别问。</span><br><span class="line"></span><br><span class="line">一般来说，在仔细挑选的公共论坛中提问，会比在私有论坛中提同样的问题更容易得到有用的回答。有几个理由可以支持这点，一是看潜在的回复者有多少，二是看观众有多少。黑客较愿意回答那些能帮助到许多人的问题。</span><br><span class="line"></span><br><span class="line">可以理解的是，老练的黑客和一些热门软件的作者正在接受过多的错发信息。就像那根最后压垮骆驼背的稻草一样，你的加入也有可能使情况走向极端 -- 已经好几次了，一些热门软件的作者从自己软件的支持中抽身出来，因为伴随而来涌入其私人邮箱的无用邮件变得无法忍受。</span><br><span class="line"></span><br><span class="line">### Stack Overflow</span><br><span class="line"></span><br><span class="line">搜索，**_然后_** 在 Stack Exchange 问。</span><br><span class="line"></span><br><span class="line">近年来，Stack Exchange community 社区已经成为回答技术及其他问题的主要渠道，尤其是那些开放源码的项目。</span><br><span class="line"></span><br><span class="line">因为 Google 索引是即时的，在看 Stack Exchange 之前先在 Google 搜索。有很高的机率某人已经问了一个类似的问题，而且 Stack Exchange 网站们往往会是搜索结果中最前面几个。如果你在 Google 上没有找到任何答案，你再到特定相关主题的网站去找。用标签（Tag）搜索能让你更缩小你的搜索结果。</span><br><span class="line"></span><br><span class="line">Stack Exchange 已经成长到[超过一百个网站](http://stackexchange.com/sites)，以下是最常用的几个站：</span><br><span class="line"></span><br><span class="line">* Super User 是问一些通用的电脑问题，如果你的问题跟代码或是写程序无关，只是一些网络连线之类的，请到这里。</span><br><span class="line">* Stack Overflow 是问写程序有关的问题。</span><br><span class="line">* Server Fault 是问服务器和网管相关的问题。</span><br><span class="line"></span><br><span class="line">### 网站和IRC论坛</span><br><span class="line"></span><br><span class="line">本地的使用者群组（user group），或者你所用的 Linux 发行版本也许正在宣传他们的网页论坛或 IRC 频道，并提供新手帮助（在一些非英语国家，新手论坛很可能还是邮件列表）， 这些地方是开始提问的好首选，特别是当你觉得遇到的也许只是相对简单或者很普通的问题时。经过宣传的 IRC 频道是公开欢迎提问的地方，通常可以即时得到回应。</span><br><span class="line"></span><br><span class="line">事实上，如果程序出的问题只发生在特定 Linux 发行版提供的版本（这很常见），最好先去该发行版的论坛或邮件列表中提问，再到程序本身的论坛或邮件列表提问。（否则）该项目的黑客可能仅仅回复 &quot;用**_我们的_**版本&quot;。</span><br><span class="line"></span><br><span class="line">在任何论坛发文以前，先确认一下有没有搜索功能。如果有，就试着搜索一下问题的几个关键词，也许这会有帮助。如果在此之前你已做过通用的网页搜索（你也该这样做），还是再搜索一下论坛，搜索引擎有可能没来得及索引此论坛的全部内容。</span><br><span class="line"></span><br><span class="line">通过论坛或 IRC 频道来提供使用者支持服务有增长的趋势，电子邮件则大多为项目开发者间的交流而保留。所以最好先在论坛或 IRC 中寻求与该项目相关的协助。</span><br><span class="line"></span><br><span class="line">### 第二步，使用项目邮件列表</span><br><span class="line"></span><br><span class="line">当某个项目提供开发者邮件列表时，要向列表而不是其中的个别成员提问，即使你确信他能最好地回答你的问题。查一查项目的文件和首页，找到项目的邮件列表并使用它。有几个很好的理由支持我们采用这种办法：</span><br><span class="line"></span><br><span class="line">  * 任何好到需要向个别开发者提出的问题，也将对整个项目群组有益。反之，如果你认为自己的问题对整个项目群组来说太愚蠢，也不能成为骚扰个别开发者的理由。</span><br><span class="line">  * 向列表提问可以分散开发者的负担，个别开发者（尤其是项目领导人）也许太忙以至于没法回答你的问题。</span><br><span class="line">  * 大多数邮件列表都会被存档，那些被存档的内容将被搜索引擎索引。如果你向列表提问并得到解答，将来其它人可以通过网页搜索找到你的问题和答案，也就不用再次发问了。</span><br><span class="line">  * 如果某些问题经常被问到，开发者可以利用此信息来改进说明文件或软件本身，以使其更清楚。如果只是私下提问，就没有人能看到最常见问题的完整场景。</span><br><span class="line"></span><br><span class="line">如果一个项目既有&quot;使用者&quot; 也有&quot;开发者&quot;（或&quot;黑客&quot;）邮件列表或论坛，而你又不会动到那些源代码，那么就向&quot;使用者&quot;列表或论坛提问。不要假设自己会在开发者列表中受到欢迎，那些人多半会将你的提问视为干扰他们开发的噪音。</span><br><span class="line"></span><br><span class="line">然而，如果你**_确信_**你的问题很特别，而且在&quot;使用者&quot; 列表或论坛中几天都没有回复，可以试试前往&quot;开发者&quot;列表或论坛发问。建议你在张贴前最好先暗地里观察几天以了解那里的行事方式（事实上这是参与任何私有或半私有列表的好主意）</span><br><span class="line"></span><br><span class="line">如果你找不到一个项目的邮件列表，而只能查到项目维护者的电子邮件地址，尽管向他发信。即使是在这种情况下，也别假设（项目）邮件列表不存在。在你的电子邮件中，请陈述你已经试过但没有找到合适的邮件列表，也提及你不反对将自己的邮件转发给他人（许多人认为，即使没什么秘密，私人电子邮件也不应该被公开。通过允许将你的电子邮件转发他人，你给了相应人员处置你邮件的选择）。</span><br><span class="line"></span><br><span class="line">### 使用有意义且描述明确的标题</span><br><span class="line"></span><br><span class="line">在邮件列表、新闻群组或论坛中，大约50字以内的标题是抓住资深专家注意力的好机会。别用喋喋不休的```帮帮忙```、```跪求```、```急```（更别说```救命啊！！！！```这样让人反感的话，用这种标题会被条件反射式地忽略）来浪费这个机会。不要妄想用你的痛苦程度来打动我们，而是在这点空间中使用极简单扼要的描述方式来提出问题。</span><br><span class="line"></span><br><span class="line">一个好标题范例是```目标 -- 差异```式的描述，许多技术支持组织就是这样做的。在```目标```部分指出是哪一个或哪一组东西有问题，在```差异```部分则描述与期望的行为不一致的地方。</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">&gt; 蠢问题：救命啊！我的笔电不能正常显示了！</span><br><span class="line"></span><br><span class="line">&gt; 聪明问题：X.org 6.8.1的鼠标游标会变形，某牌显卡 MV1005 芯片组。</span><br><span class="line"></span><br><span class="line">&gt; 更聪明问题：X.org 6.8.1的鼠标游标，在某牌显卡 MV1005 芯片组环境下 - 会变形。</span><br><span class="line"></span><br><span class="line">编写```目标 -- 差异``` 式描述的过程有助于你组织对问题的细緻思考。是什么被影响了？ 仅仅是鼠标游标或者还有其它图形？只在 X.org 的 X 版中出现？或只是出现在6.8.1版中？ 是针对某牌显卡芯片组？或者只是其中的 MV1005 型号？ 一个黑客只需瞄一眼就能够立即明白你的环境**_和_**你遇到的问题。</span><br><span class="line"></span><br><span class="line">总而言之，请想像一下你正在一个只显示标题的存档讨论串（Thread）索引中查寻。让你的标题更好地反映问题，可使下一个搜索类似问题的人能够关注这个讨论串，而不用再次提问相同的问题。</span><br><span class="line"></span><br><span class="line">如果你想在回复中提出问题，记得要修改内容标题，以表明你是在问一个问题， 一个看起来像 ```Re: 测试``` 或者 ```Re: 新bug``` 的标题很难引起足够重视。另外，在不影响连贯性之下，适当引用并删减前文的内容，能给新来的读者留下线索。</span><br><span class="line"></span><br><span class="line">对于讨论串，不要直接点击回复来开始一个全新的讨论串，这将限制你的观众。因为有些邮件阅读程序，比如 mutt ，允许使用者按讨论串排序并通过折叠讨论串来隐藏消息，这样做的人永远看不到你发的消息。</span><br><span class="line"></span><br><span class="line">仅仅改变标题还不够。mutt 和其它一些邮件阅读程序还会检查邮件标题以外的其它信息，以便为其指定讨论串。所以宁可发一个全新的邮件。</span><br><span class="line"></span><br><span class="line">在网页论坛上，好的提问方式稍有不同，因为讨论串与特定的信息紧密结合，并且通常在讨论串外就看不到里面的内容，故通过回复提问，而非改变标题是可接受的。不是所有论坛都允许在回复中出现分离的标题，而且这样做了基本上没有人会去看。不过，通过回复提问，这本身就是暧昧的做法，因为它们只会被正在查看该标题的人读到。所以，除非你**_只想_**在该讨论串当前活跃的人群中提问，不然还是另起炉灶比较好。</span><br><span class="line"></span><br><span class="line">### 使问题容易回复</span><br><span class="line"></span><br><span class="line">以```请将你的回复寄到……```来结束你的问题多半会使你得不到回答。如果你觉得花几秒钟在邮件客户端设置一下回复地址都麻烦，我们也觉得花几秒钟思考你的问题更麻烦。如果你的邮件程序不支持这样做，[换个好点的](http://linuxmafia.com/faq/Mail/muas.html)；如果是操作系统不支持这种邮件程序，也换个好点的。</span><br><span class="line"></span><br><span class="line">在论坛，要求通过电子邮件回复是非常无礼的，除非你相信回复的信息可能比较敏感（而且有人会为了某些未知的原因，只让你而不是整个论坛知道答案）。如果你只是想在有人回复讨论串时得到电子邮件提醒，可以要求网页论坛发送给你。几乎所有论坛都支持诸如```追踪此讨论串```、```有回复时发送邮件提醒```等功能。</span><br><span class="line"></span><br><span class="line">### 用清晰、正确、精准并语法正确的语句</span><br><span class="line"></span><br><span class="line">我们从经验中发现，粗心的提问者通常也会粗心的写程序与思考（我敢打包票）。回答粗心大意者的问题很不值得，我们宁愿把时间耗在别处。</span><br><span class="line"></span><br><span class="line">正确的拼字、标点符号和大小写是很重要的。一般来说，如果你觉得这样做很麻烦，不想在乎这些，那我们也觉得麻烦，不想在乎你的提问。花点额外的精力斟酌一下字句，用不着太僵硬与正式 -- 事实上，黑客文化很看重能准确地使用非正式、俚语和幽默的语句。但它**_必须很_**准确，而且有迹象表明你是在思考和关注问题。</span><br><span class="line"></span><br><span class="line">正确地拼写、使用标点和大小写，不要将```its```混淆为```it&apos;s```，```loose```搞成```lose```或者将```discrete```弄成```discreet```。不要**全部用大写**，这会被视为无礼的大声嚷嚷（全部小写也好不到哪去，因为不易阅读。[Alan Cox](http://en.wikipedia.org/wiki/Alan_Cox)也许可以这样做，但你不行。）</span><br><span class="line"></span><br><span class="line">更白话的说，如果你写得像是个半文盲[译注：[小白](http://zh.wikipedia.org/zh-tw/小白)]），那多半得不到理睬。也不要使用即时通讯中的简写或[火星文](http://zh.wikipedia.org/zh-tw/火星文)，如将```的```简化为```ㄉ```会使你看起来像一个为了少打几个键而省字的小白。更糟的是，如果像个小孩似地鬼画符那绝对是在找死，可以肯定没人会理你（或者最多是给你一大堆指责与挖苦）。</span><br><span class="line"></span><br><span class="line">如果在使用非母语的论坛提问，你可以犯点拼写和语法上的小错，但决不能在思考上马虎（没错，我们通常能弄清两者的分别）。同时，除非你知道回复者使用的语言，否则请使用英语书写。繁忙的黑客一般会直接删除用他们看不懂语言写的消息。在网络上英语是通用语言，用英语书写可以将你的问题在尚未被阅读就被直接删除的可能性降到最低。</span><br><span class="line"></span><br><span class="line">如果英文是你的外语（Second language），提示潜在回复者你有潜在的语言困难是很好的：</span><br><span class="line">[译注：以下附上原文以供使用]</span><br><span class="line"></span><br><span class="line">&gt; English is not my native language; please excuse typing errors.</span><br><span class="line"></span><br><span class="line">* 英文不是我的母语，请原谅我的错字或语法</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">&gt; If you speak $LANGUAGE, please email/PM me;</span><br><span class="line">&gt; I may need assistance translating my question.</span><br><span class="line"></span><br><span class="line">* 如果你说**某语言**，请寄信/私讯给我；我需要有人协助我翻译我的问题</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">&gt; I am familiar with the technical terms,</span><br><span class="line">&gt; but some slang expressions and idioms are difficult for me.</span><br><span class="line"></span><br><span class="line">* 我对技术名词很熟悉，但对于俗语或是特别用法比较不甚了解。</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">&gt; I&apos;ve posted my question in $LANGUAGE and English.</span><br><span class="line">&gt; I&apos;ll be glad to translate responses, if you only use one or the other.</span><br><span class="line"></span><br><span class="line">* 我把我的问题用**某语言**和英文写出来，如果你只用一种语言回答，我会乐意将其翻译成另一种。</span><br><span class="line"></span><br><span class="line">### 使用易于读取且标准的文件格式发送问题</span><br><span class="line"></span><br><span class="line">如果你人为地将问题搞得难以阅读，它多半会被忽略，人们更愿读易懂的问题，所以：</span><br><span class="line"></span><br><span class="line">  * 使用纯文字而不是HTML ([关闭HTML](http://archive.birdhouse.org/etc/evilmail.html)并不难）</span><br><span class="line">  * 使用MIME附件通常是可以的，前提是真正有内容（譬如附带的源代码或patch），而不仅仅是邮件程序生成的模板（譬如只是信件内容的拷贝）。</span><br><span class="line">  * 不要发送一段文字只是单行句子但多次断行的邮件（这使得回复部分内容非常困难）。设想你的读者是在80个字符宽的终端机上阅读邮件，最好设置你的断行点小于80字。</span><br><span class="line">  * 但是，也**_不要_**用任何固定断行资料（譬如日志档案拷贝或会话记录）。档案应该原样包含，让回复者有信心他们看到的是和你看到的一样的东西。</span><br><span class="line">  * 在英语论坛中，不要使用```Quoted-Printable``` MIME编码发送消息。这种编码对于张贴非ASCII语言可能是必须的，但很多邮件程序并不支持这种编码。当它们分断时，那些文本中四处散布的```=20```符号既难看也分散注意力，甚至有可能破坏内容的语意。</span><br><span class="line">  * 绝对，**_永远_**不要指望黑客们阅读使用封闭格式编写的文档，像是微软公司的Word或Excel文件等。大多数黑客对此的反应就像有人将还在冒热气的猪粪倒在你门口阶梯上时你的反应一样。即便他们能够处理，他们也很厌恶这么做。</span><br><span class="line">  * 如果你从使用Windows的电脑发送电子邮件，关闭微软愚蠢的```智能引号```功能 （从[选项] &gt; [校订] &gt; [自动校正选项], 按掉```智能引号```单选框），以免在你的邮件中到处散布垃圾字符。</span><br><span class="line">  * 在论坛，勿滥用```表情符号```和```HTML```功能（当它们提供时）。一两个表情符号通常没有问题，但花哨的彩色文本倾向于使人认为你是个无能之辈。过滥地使用表情符号、色彩和字体会使你看来像个傻笑的小姑娘。这通常不是个好主意，除非你只是对sex而不是有用的回复更有兴趣。</span><br><span class="line"></span><br><span class="line">如果你使用图形用户界面的邮件程序（如微软公司的Outlook或者其它类似的），注意它们的默认设置不一定满足这些要求。大多数这类程序有基于选单的```查看源代码```命令，用它来检查发送文件夹中的消息，以确保发送的是没有多余杂质的纯文本文件。</span><br><span class="line"></span><br><span class="line">### 精确的描述问题并言之有物</span><br><span class="line"></span><br><span class="line">  * 仔细、清楚地描述你的问题或Bug的症状。</span><br><span class="line">  * 描述问题发生的环境（机器配置、操作系统、应用程序、以及相关的信息），提供经销商的发行版和版本号（如：```Fedora Core 4```、```Slackware 9.1```等）。</span><br><span class="line">  * 描述在提问前你是怎样去研究和理解这个问题的。</span><br><span class="line">  * 描述在提问前为确定问题而采取的诊断步骤。</span><br><span class="line">  * 描述最近做过什么可能相关的硬件或软件变更。</span><br><span class="line">  * 尽可能的提供一个可以```重现这个问题的既定环境```的方法</span><br><span class="line"></span><br><span class="line">尽量去揣测一个黑客会怎样反问你，在他提问的时候预先给他答案。</span><br><span class="line"></span><br><span class="line">以上几点中，当你报告的是你认为可能在代码中的问题时，给黑客一个可以重现你的问题的环境尤其重要。当你这么做时，你得到有效的回答的机会和速度都会大大的提升。</span><br><span class="line"></span><br><span class="line">[Simon Tatham](http://www.chiark.greenend.org.uk/~sgtatham/)写过一篇名为《[如何有效的报告Bug](http://www.chiark.greenend.org.uk/~sgtatham/bugs-tw.html)》的出色文章。强力推荐你也读一读。</span><br><span class="line"></span><br><span class="line">### 话不在多而在精</span><br><span class="line"></span><br><span class="line">你需要提供精确有内容的信息。这并不是要求你简单的把成堆的出错代码或者资料完全转录到你的提问中。如果你有庞大而复杂的测试样例能重现程序挂掉的情境，尽量将它剪裁得越小越好。</span><br><span class="line"></span><br><span class="line">这样做的用处至少有三点。</span><br><span class="line">第一，表现出你为简化问题付出了努力，这可以使你得到回答的机会增加；</span><br><span class="line">第二，简化问题使你更有可能得到**_有用_**的答案；</span><br><span class="line">第三，在精炼你的bug报告的过程中，你很可能就自己找到了解决方法或权宜之计。</span><br><span class="line"></span><br><span class="line">### 别动辄声称找到Bug</span><br><span class="line"></span><br><span class="line">当你在使用软件中遇到问题，除非你非常、**_非常_**的有根据，不要动辄声称找到了Bug。提示：除非你能提供解决问题的源代码补丁，或者对前一版本的回归测试表现出不正确的行为，否则你都多半不够完全确信。这同样适用在网页和文件，如果你（声称）发现了文件的```Bug```，你应该能提供相应位置的修正或替代文件。</span><br><span class="line"></span><br><span class="line">请记得，还有许多其它使用者没遇到你发现的问题，否则你在阅读文件或搜索网页时就应该发现了（你在抱怨前[已经做了这些，是吧](#在提问之前)？）。这也意味着很有可能是你弄错了而不是软件本身有问题。</span><br><span class="line"></span><br><span class="line">编写软件的人总是非常辛苦地使它尽可能完美。如果你声称找到了Bug，也就是在质疑他们的能力，即使你是对的，也有可能会冒犯到其中某部分人。这尤其严重当你在标题中嚷嚷着有```Bug```。</span><br><span class="line"></span><br><span class="line">提问时，即使你私下非常确信已经发现一个真正的Bug，最好写得像是**_你_**做错了什么。如果真的有Bug，你会在回复中看到这点。这样做的话，如果真有Bug，维护者就会向你道歉，这总比你惹恼别人然后欠别人一个道歉要好一点。</span><br><span class="line"></span><br><span class="line">### 可以低声下气，但还是要先做功课</span><br><span class="line"></span><br><span class="line">有些人明白他们不该粗鲁或傲慢的提问并要求得到答复，但他们选择另一个极端 -- 低声下气：```我知道我只是个可悲的新手，一个撸瑟，但...```。这既使人困扰，也没有用，尤其是伴随着与实际问题含糊不清的描述时更令人反感。</span><br><span class="line"></span><br><span class="line">别用原始灵长类动物的把戏来浪费你我的时间。取而代之的是，尽可能清楚地描述背景条件和你的问题情况。这比低声下气更好地定位了你的位置。</span><br><span class="line"></span><br><span class="line">有时网页论坛会设有专为新手提问的版面，如果你真的认为遇到了初学者的问题，到那去就是了，但一样别那么低声下气。</span><br><span class="line"></span><br><span class="line">### 描述问题症状而非猜测</span><br><span class="line"></span><br><span class="line">告诉黑客们你认为问题是怎样造成的并没什么帮助。（如果你的推断如此有效，还用向别人求助吗？），因此要确信你原原本本告诉了他们问题的症状，而不是你的解释和理论；让黑客们来推测和诊断。如果你认为陈述自己的猜测很重要，清楚地说明这只是你的猜测，并描述为什么它们不起作用。</span><br><span class="line"></span><br><span class="line">***蠢问题***</span><br><span class="line"></span><br><span class="line">&gt; 我在编译内核时接连遇到 SIG11 错误，</span><br><span class="line">&gt; 我怀疑某条飞线搭在主板的走线上了，这种情况应该怎样检查最好？</span><br><span class="line"></span><br><span class="line">***聪明问题***</span><br><span class="line">&gt; 我的组装电脑是 FIC-PA2007 主机板搭载 AMD K6/233 CPU（威盛 Apollo VP2芯片组），</span><br><span class="line">&gt; 256MB Corsair PC133 SDRAM内存，在编译内核时，从开机20分钟以后就频频产生 SIG11 错误，</span><br><span class="line">&gt; 但是在头20分钟内从没发生过相同的问题。重新启动也没有用，但是关机一晚上就又能工作20分钟。</span><br><span class="line">&gt; 所有内存都换过了，没有效果。相关部分的标准编译记录如下…。</span><br><span class="line"></span><br><span class="line">由于以上这点似乎让许多人觉得难以配合，这里有句话可以提醒你：```所有的诊断专家都来自密苏里州。``` 美国国务院的官方座右铭则是：```让我看看```（出自国会议员 Willard D. Vandiver 在1899年时的讲话：```我来自一个出产玉米，棉花，牛蒡和民主党人的国家，滔滔雄辩既不能说服我，也不会让我满意。我来自密苏里州，你必须让我看看。```） 针对诊断者而言，这并不是一种怀疑，而只是一种真实而有用的需求，以便让他们看到的是与你看到的原始证据尽可能一致的东西，而不是你的猜测与归纳的结论。所以，大方的展示给我们看吧！</span><br><span class="line"></span><br><span class="line">### 按发生时间先后列出问题症状</span><br><span class="line"></span><br><span class="line">问题发生前的一系列操作，往往就是对找出问题最有帮助的线索。因此，你的说明里应该包含你的操作步骤，以及机器和软件的反应，直到问题发生。在命令行处理的情况下，提供一段操作记录（例如运行脚本工具所生成的），并引用相关的若干行（如20行）记录会非常有帮助。</span><br><span class="line"></span><br><span class="line">如果挂掉的程序有诊断选项（如 -v 的详述开关），试着选择这些能在记录中增加调试信息的选项。记住，```多```不等于```好```。试着选取适当的调试级别以便提供有用的信息而不是让读者淹没在垃圾中。</span><br><span class="line"></span><br><span class="line">如果你的说明很长（如超过四个段落），在开头简述问题，接下来再按时间顺序详述会有所帮助。这样黑客们在读你的记录时就知道该注意哪些内容了。</span><br><span class="line"></span><br><span class="line">### 描述目标而不是过程</span><br><span class="line"></span><br><span class="line">如果你想弄清楚如何做某事（而不是报告一个Bug），在开头就描述你的目标，然后才陈述重现你所卡住的特定步骤。</span><br><span class="line"></span><br><span class="line">经常寻求技术帮助的人在心中有个更高层次的目标，而他们在自以为能达到目标的特定道路上被卡住了，然后跑来问该怎么走，但没有意识到这条路本身就有问题。结果要费很大的劲才能搞定。</span><br><span class="line"></span><br><span class="line">**蠢问题**</span><br><span class="line">&gt; 我怎样才能从某绘图程序的颜色选择器中取得十六进制的的RGB值？</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">**聪明问题**</span><br><span class="line">&gt; 我正试着用替换一幅图片的色码成自己选定的色码，我现在知道的唯一方法是编辑每个色码区块，</span><br><span class="line">&gt; 但却无法从某绘图程序的颜色选择器取得十六进制的的RGB值。</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">第二种提问法比较聪明，你可能得到像是```建议采用另一个更合适的工具```的回复。</span><br><span class="line"></span><br><span class="line">### 别要求使用私人电邮回复</span><br><span class="line"></span><br><span class="line">黑客们认为问题的解决过程应该公开、透明，此过程中如果更有经验的人注意到不完整或者不当之处，最初的回复才能够、也应该被纠正。同时，作为提供帮助者也能因为能力和学识被其它同行看到而得到某种奖励。</span><br><span class="line"></span><br><span class="line">当你要求私下回复时，这个过程和奖励都被中止。别这样做，让**_回复者_**来决定是否私下回答 -- 如果他真这么做了，通常是因为他认为问题编写太差或者太肤浅，以至于对其它人没有兴趣。</span><br><span class="line"></span><br><span class="line">这条规则存在一条有限的例外，如果你确信提问可能会引来大量雷同的回复时，那么这个神奇的提问句会是```向我发电邮，我将为论坛归纳这些回复```。试着将邮件列表或新闻群组从洪水般的雷同回复中解救出来是非常有礼貌的 -- 但你必须信守诺言。</span><br><span class="line"></span><br><span class="line">### 清楚明确的表达你的问题以及需求</span><br><span class="line"></span><br><span class="line">漫无边际的提问近乎无休无止的时间黑洞。最有可能给你有用答案的人通常也正是最忙的人（他们忙是因为要亲自完成大部分工作）。这样的人对无节制的时间黑洞相当厌恶，所以他们也倾向于厌恶那些漫无边际的提问。</span><br><span class="line"></span><br><span class="line">如果你明确表述需要回答者做什么（如提供指点、发送一段代码、检查你的补丁、或是其他等等），就最有可能得到有用的答案。因为这会定出一个时间和精力的上限，便于回答者能集中精力来帮你。这么做很棒。</span><br><span class="line"></span><br><span class="line">要理解专家们所处的世界，请把专业技能想像为充裕的资源，而回复的时间则是稀缺的资源。你要求他们奉献的时间越少，你越有可能从真正专业而且很忙的专家那里得到解答。</span><br><span class="line"></span><br><span class="line">所以，界定一下你的问题，使专家花在辨识你的问题和回答所需要付出的时间减到最少，这技巧对你有用答案相当有帮助 -- 但这技巧通常和简化问题有所区别。因此，问```我想更好的理解X，可否指点一下哪有好一点说明？```通常比问```你能解释一下X吗？```更好。如果你的代码不能运作，通常请别人看看哪里有问题，比要求别人替你改正要明智得多。</span><br><span class="line"></span><br><span class="line">### 询问有关代码的问题时</span><br><span class="line"></span><br><span class="line">别要求他人帮你有问题的代码调试而不提示一下应该从何入手。张贴几百行的代码，然后说一声：```它不会动```会让你完全被忽略。只贴几十行代码，然后说一句：```在第七行以后，我期待它显示 &lt;x&gt;，但实际出现的是 &lt;y&gt;```比较有可能让你得到回应。</span><br><span class="line"></span><br><span class="line">最有效描述程序问题的方法是提供最精简的Bug展示测试示例（bug-demonstrating test case）。什么是最精简的测试示例? 那是问题的缩影；一小个程序片段能**刚好**展示出程序的异常行为，而不包含其他令人分散注意力的内容。怎么制作最精简的测试示例？如果你知道哪一行或哪一段代码会造成异常的行为，复制下来并加入足够重现这个状况的代码（例如，足以让这段代码能被编译/直译/被应用程序处理）。如果你无法将问题缩减到一个特定区块，就复制一份代码并移除不影响产生问题行为的部分。总之，测试示例越小越好（查看[话不在多而在精](#话不在多而在精)一节）。</span><br><span class="line"></span><br><span class="line">一般而言，要得到一段相当精简的测试示例并不太容易，但永远先尝试这样做的是种好习惯。这种方式可以帮助你了解如何自行解决这个问题 —- 而且即使你的尝试不成功，黑客们也会看到你在尝试取得答案的过程中付出了努力，这可以让他们更愿意与你合作。</span><br><span class="line"></span><br><span class="line">如果你只是想让别人帮忙审查（Review）一下代码，在信的开头就要说出来，并且一定要提到你认为哪一部分特别需要关注以及为什么。</span><br><span class="line"></span><br><span class="line">### 别把自己家庭作业的问题贴上来</span><br><span class="line"></span><br><span class="line">黑客们很擅长分辨哪些问题是家庭作业式的问题；因为我们中的大多数都曾自己解决这类问题。同样，这些问题得由**_你_**来搞定，你会从中学到东西。你可以要求给点提示，但别要求得到完整的解决方案。</span><br><span class="line"></span><br><span class="line">如果你怀疑自己碰到了一个家庭作业式的问题，但仍然无法解决，试试在使用者群组，论坛或（最后一招）在项目的**使用者**邮件列表或论坛中提问。尽管黑客们**_会_**看出来，但一些有经验的使用者也许仍会给你一些提示。</span><br><span class="line"></span><br><span class="line">### 去掉无意义的提问句</span><br><span class="line"></span><br><span class="line">避免用无意义的话结束提问，例如```有人能帮我吗？```或者```这有答案吗？```。</span><br><span class="line"></span><br><span class="line">首先：如果你对问题的描述不是很好，这样问更是画蛇添足。</span><br><span class="line"></span><br><span class="line">其次：由于这样问是画蛇添足，黑客们会很厌烦你 -- 而且通常会用逻辑上正确，但毫无意义的回答来表示他们的蔑视， 例如：```没错，有人能帮你```或者```不，没答案```。</span><br><span class="line"></span><br><span class="line">一般来说，避免用 ```是或否```、```对或错```、```有或没有```类型的问句，除非你想得到[是或否类型的回答](http://homepage.ntlworld.com./jonathan.deboynepollard/FGA/questions-with-yes-or-no-answers.html)。</span><br><span class="line"></span><br><span class="line">### 即使你很急也不要在标题写```紧急</span><br></pre></td></tr></table></figure></p><p>这是你的问题，不是我们的。宣称<figure class="highlight plain"><figcaption><span>-- 你希望能看到你问题的人可能永远也看不到。</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">有半个例外的情况是，如果你是在一些很高调，会使黑客们兴奋的地方，也许值得这样去做。在这种情况下，如果你有时间压力，也很有礼貌地提到这点，人们也许会有兴趣回答快一点。</span><br><span class="line"></span><br><span class="line">当然，这风险很大，因为黑客们兴奋的点多半与你的不同。譬如从 NASA 国际空间站（International Space Station）发这样的标题没有问题，但用自我感觉良好的慈善行为或政治原因发肯定不行。事实上，张贴诸如```紧急：帮我救救这个毛绒绒的小海豹！```肯定让你被黑客忽略或惹恼他们，即使他们认为毛绒绒的小海豹很重要。</span><br><span class="line"></span><br><span class="line">如果你觉得这点很不可思议，最好再把这份指南剩下的内容多读几遍，直到你弄懂了再发文。</span><br><span class="line"></span><br><span class="line">### 礼多人不怪，而且有时还很有帮助</span><br><span class="line"></span><br><span class="line">彬彬有礼，多用```请```和```谢谢您的关注```，或```谢谢你的关照```。让大家都知道你对他们花时间免费提供帮助心存感激。</span><br><span class="line"></span><br><span class="line">坦白说，这一点并没有比清晰、正确、精准并合法语法和避免使用专用格式重要（也不能取而代之）。黑客们一般宁可读有点唐突但技术上鲜明的Bug报告，而不是那种有礼但含糊的报告。（如果这点让你不解，记住我们是按问题能教我们什么来评价问题的价值的）</span><br><span class="line"></span><br><span class="line">然而，如果你有一串的问题待解决，客气一点肯定会增加你得到有用回应的机会。</span><br><span class="line"></span><br><span class="line">（我们注意到，自从本指南发布后，从资深黑客那里得到的唯一严重缺陷反馈，就是对预先道谢这一条。一些黑客觉得```先谢了```意味着事后就不用再感谢任何人的暗示。我们的建议是要么先说```先谢了```，**_然后_**事后再对回复者表示感谢，或者换种方式表达感激，譬如用```谢谢你的关注```或```谢谢你的关照```。）</span><br><span class="line"></span><br><span class="line">### 问题解决后，加个简短的补充说明</span><br><span class="line"></span><br><span class="line">问题解决后，向所有帮助过你的人发个说明，让他们知道问题是怎样解决的，并再一次向他们表示感谢。如果问题在新闻组或者邮件列表中引起了广泛关注，应该在那里贴一个说明比较恰当。</span><br><span class="line"></span><br><span class="line">最理想的方式是向最初提问的话题回复此消息，并在标题中包含```已修正```，```已解决```或其它同等含义的明显标记。在人来人往的邮件列表里，一个看见讨论串```问题 X```和```问题的X - 已解决```的潜在回复者就明白不用再浪费时间了（除非他个人觉得```问题 X```的有趣），因此可以利用此时间去解决其它问题。</span><br><span class="line"></span><br><span class="line">补充说明不必很长或是很深入；简单的一句```你好，原来是网线出了问题！谢谢大家 – Bill```比什么也不说要来的好。事实上，除非结论真的很有技术含量，否则简短可爱的小结比长篇大论更好。说明问题是怎样解决的，但大可不必将解决问题的过程复述一遍。</span><br><span class="line"></span><br><span class="line">对于有深度的问题，张贴调试记录的摘要是有帮助的。描述问题的最终状态，说明是什么解决了问题，在此**_之后_**才指明可以避免的盲点。避免盲点的部分应放在正确的解决方案和其它总结材料之后，而不要将此信息搞成侦探推理小说。列出那些帮助过你的名字，会让你交到更多朋友。</span><br><span class="line"></span><br><span class="line">除了有礼貌和有内涵以外，这种类型的补充也有助于他人在邮件列表/新闻群组/论坛中搜索到真正解决你问题的方案，让他们也从中受益。</span><br><span class="line"></span><br><span class="line">至少，这种补充有助于让每位参与协助的人因问题的解决而从中得到满足感。如果你自己不是技术专家或者黑客，那就相信我们，这种感觉对于那些你向他们求助的大师或者专家而言，是非常重要的。问题悬而未决会让人灰心；黑客们渴望看到问题被解决。好人有好报，满足他们的渴望，你会在下次提问时尝到甜头。</span><br><span class="line"></span><br><span class="line">思考一下怎样才能避免他人将来也遇到类似的问题，自问写一份文件或加个常见问题（FAQ）会不会有帮助。如果是的话就将它们发给维护者。</span><br><span class="line"></span><br><span class="line">在黑客中，这种良好的后继行动实际上比传统的礼节更为重要，也是你如何透过善待他人而赢得声誉的方式，这是非常有价值的资产。</span><br><span class="line"></span><br><span class="line">## 如何解读答案</span><br><span class="line"></span><br><span class="line">&lt;a id=&quot;RTFM&quot;&gt;&lt;/a&gt;</span><br><span class="line">### RTFM和STFW：如何知道你已完全搞砸了</span><br><span class="line"></span><br><span class="line">有一个古老而神圣的传统：如果你收到```RTFM （Read The Fucking Manual）```的回应，回答者认为你**应该去读他妈的手册**。当然，基本上他是对的，你应该去读一读。</span><br><span class="line"></span><br><span class="line">RTFM 有一个年轻的亲戚。如果你收到```STFW（Search The Fucking Web）```的回应，回答者认为你**应该到他妈的网上搜索**过了。那人多半也是对的，去搜索一下吧。（更温和一点的说法是 **[Google是你的朋友](http://lmgtfy.com/)**！）</span><br><span class="line"></span><br><span class="line">在论坛，你也可能被要求去爬爬论坛的旧文。事实上，有人甚至可能热心地为你提供以前解决此问题的讨论串。但不要依赖这种关照，提问前应该先搜索一下旧文。</span><br><span class="line"></span><br><span class="line">通常，用这两句之一回答你的人会给你一份包含你需要内容的手册或者一个网址，而且他们打这些字的时候也正在读着。这些答复意味着回答者认为</span><br><span class="line"></span><br><span class="line">  * **你需要的信息非常容易获得**；</span><br><span class="line">  * **你自己去搜索这些信息比灌给你能让你学到更多**。</span><br><span class="line"></span><br><span class="line">你不应该因此不爽；**依照黑客的标准，他已经表示了对你一定程度的关注，而没有对你的要求视而不见**。你应该对他祖母般的慈祥表示感谢。</span><br><span class="line"></span><br><span class="line">### 如果还是搞不懂</span><br><span class="line"></span><br><span class="line">如果你看不懂回应，别立刻要求对方解释。像你以前试着自己解决问题时那样（利用手册，FAQ，网络，身边的高手），先试着去搞懂他的回应。如果你真的需要对方解释，记得表现出你已经从中学到了点什么。</span><br><span class="line"></span><br><span class="line">比方说，如果我回答你：```看来似乎是 zentry 卡住了；你应该先清除它。```，然后，这是一个**_很糟的_**后续问题回应：```zentry是什么？``` **_好_**的问法应该是这样：```哦~~~我看过说明了但是只有 -z 和 -p 两个参数中提到了 zentries，而且还都没有清楚的解释如何清除它。你是指这两个中的哪一个吗？还是我看漏了什么？</span><br></pre></td></tr></table></figure></p><h3 id="处理无礼的回应"><a href="#处理无礼的回应" class="headerlink" title="处理无礼的回应"></a>处理无礼的回应</h3><p>很多黑客圈子中看似无礼的行为并不是存心冒犯。相反，它是直接了当，一针见血式的交流风格，这种风格更注重解决问题，而不是使人感觉舒服而却模模糊糊。</p><p>如果你觉得被冒犯了，试着平静地反应。如果有人真的做了出格的事，邮件列表、新闻群组或论坛中的前辈多半会招呼他。如果这<strong>_没有_</strong>发生而你却发火了，那么你发火对象的言语可能在黑客社区中看起来是正常的，而<strong>_你_</strong>将被视为有错的一方，这将伤害到你获取信息或帮助的机会。</p><p>另一方面，你偶而真的会碰到无礼和无聊的言行。与上述相反，对真正的冒犯者狠狠地打击，用犀利的语言将其驳得体无完肤都是可以接受的。然而，在行事之前一定要非常非常的有根据。纠正无礼的言论与开始一场毫无意义的口水战仅一线之隔，黑客们自己莽撞地越线的情况并不鲜见。如果你是新手或外人，避开这种莽撞的机会并不高。如果你想得到的是信息而不是消磨时光，这时最好不要把手放在键盘上以免冒险。</p><p>（有些人断言很多黑客都有轻度的自闭症或亚斯伯格综合症，缺少用于润滑人类社会<strong>正常</strong>交往所需的神经。这既可能是真也可能是假的。如果你自己不是黑客，兴许你认为我们脑袋有问题还能帮助你应付我们的古怪行为。只管这么干好了，我们不在乎。我们<strong>_喜欢_</strong>我们现在这个样子，并且通常对病患标记都有站得住脚的怀疑。）</p><p>在下一节，我们会谈到另一个问题，当<strong>_你_</strong>行为不当时所会受到的<code>冒犯</code>。</p><h2 id="如何避免扮演失败者"><a href="#如何避免扮演失败者" class="headerlink" title="如何避免扮演失败者"></a>如何避免扮演失败者</h2><p>在黑客社区的论坛中有那么几次你可能会搞砸 – 以本指南所描述到的或类似的方式。而你会在公开场合中被告知你是如何搞砸的，也许攻击的言语中还会带点夹七夹八的颜色。</p><p>这种事发生以后，你能做的最糟糕的事莫过于哀嚎你的遭遇、宣称被口头攻击、要求道歉、高声尖叫、憋闷气、威胁诉诸法律、向其雇主报怨、忘了关马桶盖等等。相反地，你该这么做：</p><p>熬过去，这很正常。事实上，它是有益健康且合理的。</p><p>社区的标准不会自行维持，它们是通过参与者积极而<strong><em>公开地</em></strong>执行来维持的。不要哭嚎所有的批评都应该通过私下的邮件传送，它不是这样运作的。当有人评论你的一个说法有误或者提出不同看法时，坚持声称受到个人攻击也毫无益处，这些都是失败者的态度。</p><p>也有其它的黑客论坛，受过高礼节要求的误导，禁止参与者张贴任何对别人帖子挑毛病的消息，并声称<code>如果你不想帮助用户就闭嘴。</code> 结果造成有想法的参与者纷纷离开，这么做只会使它们沦为毫无意义的嘮叨与无用的技术论坛。</p><p>夸张的讲法是：你要的是<strong>友善</strong>（以上述方式）还是有用？两个里面挑一个。</p><p>记着：当黑客说你搞砸了，并且（无论多么刺耳）告诉你别再这样做时，他正在为关心<strong>你</strong>和<strong>他的社区</strong>而行动。对他而言，不理你并将你从他的生活中滤掉更简单。如果你无法做到感谢，至少要表现地有点尊严，别大声哀嚎，也别因为自己是个有戏剧性超级敏感的灵魂和自以为有资格的新来者，就指望别人像对待脆弱的洋娃娃那样对你。</p><p>有时候，即使你没有搞砸（或者只是在他的想像中你搞砸了），有些人也会无缘无故地攻击你本人。在这种情况下，抱怨倒是<strong>_真的_</strong>会把问题搞砸。</p><p>这些来找麻烦的人要么是毫无办法但自以为是专家的不中用家伙，要么就是测试你是否真会搞砸的心理专家。其它读者要么不理睬，要么用自己的方式对付他们。这些来找麻烦的人在给他们自己找麻烦，这点你不用操心。</p><p>也别让自己卷入口水战，最好不要理睬大多数的口水战 – 当然，是在你检验它们只是口水战，而并未指出你有搞砸的地方，且也没有巧妙地将问题真正的答案藏于其后（这也是有可能的）。</p><h2 id="不该问的问题"><a href="#不该问的问题" class="headerlink" title="不该问的问题"></a>不该问的问题</h2><p>以下是几个经典蠢问题，以及黑客没回答时心中所想的：</p><p>问题：<a href="#q1">我能在哪找到 X 程序或 X 资源？</a></p><p>问题：<a href="#q2">我怎样用 X 做 Y？</a></p><p>问题：<a href="#q3">如何设定我的 shell 提示？</a></p><p>问题：<a href="#q4">我可以用 Bass-o-matic 文件转换工具将 AcmeCorp 档案转换为 TeX 格式吗？</a></p><p>问题：<a href="#q5">我的程序/设定/SQL语句没有用</a></p><p>问题：<a href="#q6">我的 Windows 电脑有问题，你能帮我吗？</a></p><p>问题：<a href="#q7">我的程序不会动了，我认为系统工具 X 有问题</a></p><p>问题：<a href="#q8">我在安装 Linux（或者 X ）时有问题，你能帮我吗？</a></p><p>问题：<a href="#q9">我怎么才能破解 root 帐号/窃取 OP 特权/读别人的邮件呢？</a></p><hr><p><a id="q1"></a></p><blockquote><p>问题：我能在哪找到 X 程序或 X 资源？</p></blockquote><p>回答：就在我找到它的地方啊，白痴 – 搜索引擎的那一头。天哪！难道还有人不会用 <a href="http://www.google.com" target="_blank" rel="noopener">Google</a> 吗？</p><p><a id="q2"></a></p><blockquote><p>问题：我怎样用 X 做 Y？</p></blockquote><p>回答：如果你想解决的是 Y ，提问时别给出可能并不恰当的方法。这种问题说明提问者不但对 X 完全无知，也对 Y 要解决的问题糊涂，还被特定形势禁锢了思维。最好忽略这种人，等他们把问题搞清楚了再说。</p><p><a id="q3"></a></p><blockquote><p>问题：如何设定我的 shell 提示？？</p></blockquote><p>回答：如果你有足够的智慧提这个问题，你也该有足够的智慧去 <a href="#RTFM">RTFM</a>，然后自己去找出来。</p><p><a id="q4"></a></p><blockquote><p>问题：我可以用 Bass-o-matic 文件转换工具将 AcmeCorp 档案转换为 TeX 格式吗？</p></blockquote><p>回答：试试看就知道了。如果你试过，你既知道了答案，就不用浪费我的时间了。</p><p><a id="q5"></a></p><blockquote><p>问题：我的程序/设定/SQL语句没有用</p></blockquote><p>回答：这不算是问题吧，我对要我问你二十个问题才找得出你真正问题的问题没兴趣 – 我有更有意思的事要做呢。在看到这类问题的时候，我的反应通常不外如下三种</p><ul><li>你还有什么要补充的吗？</li><li>真糟糕，希望你能搞定。</li><li>这关我有什么屁事？</li></ul><p><a id="q6"></a></p><blockquote><p>问题：我的 Windows 电脑有问题，你能帮我吗？</p></blockquote><p>回答：能啊，扔掉萎软的垃圾，换个像 Linux 或 BSD 的开放源代码操作系统吧。</p><p>注意：如果程序有官方版 Windows 或者与 Windows 有互动（如Samba），你<strong>_可以_</strong>问与Windows相关的问题， 只是别对问题是由 Windows 操作系统而不是程序本身造成的回复感到惊讶， 因为 Windows 一般来说实在太烂，这种说法通常都是对的。</p><p><a id="q7"></a></p><blockquote><p>问题：我的程序不会动了，我认为系统工具 X 有问题</p></blockquote><p>回答：你完全有可能是第一个注意到被成千上万用户反复使用的系统调用与函数库档案有明显缺陷的人，更有可能的是你完全没有根据。不同凡响的说法需要不同凡响的证据，当你这样声称时，你必须有清楚而详尽的缺陷说明文件作后盾。</p><p><a id="q8"></a></p><blockquote><p>问题：我在安装 Linux（或者 X ）时有问题，你能帮我吗？</p></blockquote><p>回答：不能，我只有亲自在你的电脑上动手才能找到毛病。还是去找你当地的 Linux 使用群组者寻求实际的指导吧（你能在<a href="http://www.linux.org/groups/index.html" target="_blank" rel="noopener">这儿</a>找到使用者群组的清单）。</p><p>注意：如果安装问题与某 Linux 的发行版有关，在它的邮件列表、论坛或本地使用者群组中提问也许是恰当的。此时，应描述问题的准确细节。在此之前，先用 <code>Linux</code> 和<strong>_所有_</strong>被怀疑的硬件作关键词仔细搜索。</p><p><a id="q9"></a></p><blockquote><p>问题：我怎么才能破解 root 帐号/窃取 OP 特权/读别人的邮件呢？</p></blockquote><p>回答：想要这样做，说明了你是个卑鄙小人；想找个黑客帮你，说明你是个白痴！</p><h2 id="好问题与蠢问题"><a href="#好问题与蠢问题" class="headerlink" title="好问题与蠢问题"></a>好问题与蠢问题</h2><p>最后，我将透过举一些例子，来说明怎样聪明的提问；同一个问题的两种问法被放在一起，一种是愚蠢的，另一种才是明智的。</p><p><strong><em>蠢问题</em></strong>：</p><blockquote><p>我可以在哪儿找到关于 Foonly Flurbamatic 的资料？</p></blockquote><p>这种问法无非想得到 <a href="#RTFM">STFW</a> 这样的回答。</p><p><strong><em>聪明问题</em></strong>：</p><blockquote><p>我用Google搜索过 “Foonly Flurbamatic 2600”，但是没找到有用的结果。谁知道上哪儿去找对这种设备编程的资料？</p></blockquote><p>这个问题已经 STFW 过了，看起来他真的遇到了麻烦。</p><p><strong><em>蠢问题</em></strong></p><blockquote><p>我从 foo 项目找来的源码没法编译。它怎么这么烂？</p></blockquote><p>他觉得都是别人的错，这个傲慢自大的提问者</p><p><strong><em>聪明问题</em></strong></p><blockquote><p>foo 项目代码在 Nulix 6.2 版下无法编译通过。我读过了 FAQ，但里面没有提到跟 Nulix 有关的问题。这是我编译过程的记录，我有什么做的不对的地方吗？</p></blockquote><p>提问者已经指明了环境，也读过了FAQ，还列出了错误，并且他没有把问题的责任推到别人头上，他的问题值得被关注。</p><p><strong><em>蠢问题</em></strong></p><blockquote><p>我的主机板有问题了，谁来帮我？</p></blockquote><p>某黑客对这类问题的回答通常是：<code>好的，还要帮你拍拍背和换尿布吗？</code>，然后按下删除键。</p><p><strong><em>聪明问题</em></strong></p><blockquote><p>我在 S2464 主机板上试过了 X 、 Y 和 Z ，但没什么作用，我又试了 A 、 B 和 C 。请注意当我尝试 C 时的奇怪现象。显然 florbish 正在 grommicking，但结果出人意料。通常在 Athlon MP 主机板上引起 grommicking 的原因是什么？有谁知道接下来我该做些什么测试才能找出问题？</p></blockquote><p>这个家伙，从另一个角度来看，值得去回答他。他表现出了解决问题的能力，而不是坐等天上掉答案。</p><p>在最后一个问题中，注意<code>告诉我答案</code>和<code>给我启示，指出我还应该做什么诊断工作</code>之间微妙而又重要的区别。</p><p>事实上，后一个问题源自于 2001 年 8 月在 Linux 内核邮件列表（lkml）上的一个真实的提问。我（Eric）就是那个提出问题的人。我在 Tyan S2464 主板上观察到了这种无法解释的锁定现象，列表成员们提供了解决这一问题的重要信息。</p><p>通过我的提问方法，我给了别人可以咀嚼玩味的东西；我设法让人们很容易参与并且被吸引进来。我显示了自己具备和他们同等的能力，并邀请他们与我共同探讨。通过告诉他们我所走过的弯路，以避免他们再浪费时间，我也表明了对他们宝贵时间的尊重。</p><p>事后，当我向每个人表示感谢，并且赞赏这次良好的讨论经历的时候， 一个 Linux 内核邮件列表的成员表示，他觉得我的问题得到解决并非由于我是这个列表中的<strong>_名人_</strong>，而是因为我用了正确的方式来提问。</p><p>黑客从某种角度来说是拥有丰富知识但缺乏人情味的家伙；我相信他是对的，如果我<strong>_像_</strong>个乞讨者那样提问，不论我是谁，一定会惹恼某些人或者被他们忽视。他建议我记下这件事，这直接导致了本指南的出现。</p><h2 id="如果得不到回答"><a href="#如果得不到回答" class="headerlink" title="如果得不到回答"></a>如果得不到回答</h2><p>如果仍得不到回答，请不要以为我们觉得无法帮助你。有时只是看到你问题的人不知道答案罢了。没有回应不代表你被忽视，虽然不可否认这种差别很难区分。</p><p>总的来说，简单的重复张贴问题是个很糟的点子。这将被视为无意义的喧闹。有点耐心，知道你问题答案的人可能生活在不同的时区，可能正在睡觉，也有可能你的问题一开始就没有组织好。</p><p>你可以通过其他渠道获得帮助，这些渠道通常更适合初学者的需要。</p><p>有许多网上的以及本地的使用者群组，由热情的软件爱好者（即使他们可能从没亲自写过任何软件）组成。通常人们组建这样的团体来互相帮助并帮助新手。</p><p>另外，你可以向很多商业公司寻求帮助，不论公司大还是小。别为要付费才能获得帮助而感到沮丧！毕竟，假使你的汽车发动机汽缸密封圈爆掉了– 完全可能如此 –你还得把它送到修车铺，并且为维修付费。就算软件没花费你一分钱，你也不能强求技术支持总是免费的。</p><p>对像是 Linux 这种大众化的软件，每个开发者至少会对应到上万名使用者。根本不可能由一个人来处理来自上万名使用者的求助电话。要知道，即使你要为这些协助付费，和你所购买的同类软件相比，你所付出的也是微不足道的（通常封闭源代码软件的技术支持费用比开放源代码软件的要高得多，且内容也没那么丰富）。</p><h2 id="如何更好地回答问题"><a href="#如何更好地回答问题" class="headerlink" title="如何更好地回答问题"></a>如何更好地回答问题</h2><p><strong><em>态度和善一点</em></strong>。问题带来的压力常使人显得无礼或愚蠢，其实并不是这样。</p><p><strong><em>对初犯者私下回复</em></strong>。对那些坦诚犯错之人没有必要当众羞辱，一个真正的新手也许连怎么搜索或在哪找常见问题都不知道。</p><p><strong><em>如果你不确定，一定要说出来</em></strong>！一个听起来权威的错误回复比没有还要糟，别因为听起来像个专家很好玩，就给别人乱指路。要谦虚和诚实，给提问者与同行都树个好榜样。</p><p><strong><em>如果帮不了忙，也别妨碍他</em></strong>。不要在实际步骤上开玩笑，那样也许会毁了使用者的设置 –有些可怜的呆瓜会把它当成真的指令。</p><p><strong><em>试探性的反问以引出更多的细节</em></strong>。如果你做得好，提问者可以学到点东西 –你也可以。试试将蠢问题转变成好问题，别忘了我们都曾是新手。</p><p>尽管对那些懒虫抱怨一声 RTFM 是正当的，能指出文件的位置（即使只是建议个 Google 搜索关键词）会更好。</p><p><strong><em>如果你决定回答，就请给出好的答案</em></strong>。当别人正在用错误的工具或方法时别建议笨拙的权宜之计（wordaround），应推荐更好的工具，重新界定问题。</p><p><strong><em>正面的回答问题</em></strong>！如果这个提问者已经很深入的研究而且也表明已经试过 X 、 Y 、 Z 、 A 、 B 、 C 但没得到结果，回答 <code>试试看 A 或是 B</code> 或者 <code>试试X 、 Y 、 Z 、 A 、 B 、 C</code> 并附上一个链接一点用都没有。</p><p><strong><em>帮助你的社区从问题中学习</em></strong>。当回复一个好问题时，问问自己<code>如何修改相关文件或常见问题文件以免再次解答同样的问题？</code>，接着再向文件维护者发一份补丁。</p><p>如果你是在研究一番后才做出的回答，<strong><em>展现你的技巧而不是直接端出结果</em></strong>。毕竟<code>授人以鱼不如授人以渔</code>。</p><h2 id="相关资源"><a href="#相关资源" class="headerlink" title="相关资源"></a>相关资源</h2><p>如果你需要个人电脑、Unix 系统和网络如何运作的基础知识，参阅<a href="http://en.tldp.org/HOWTO/Unix-and-Internet-Fundamentals-HOWTO/" target="_blank" rel="noopener">Unix系统和网络基本原理</a>。</p><p>当你发布软件或补丁时，试着按<a href="http://en.tldp.org/HOWTO/Software-Release-Practice-HOWTO/index.html" target="_blank" rel="noopener">软件发布实践</a>操作。</p><h2 id="鸣谢"><a href="#鸣谢" class="headerlink" title="鸣谢"></a>鸣谢</h2><p>Evelyn Mitchel贡献了一些愚蠢问题例子并启发了编写<code>如何更好地回答问题</code>这一节， Mikhail Ramendik贡献了一些特别有价值的建议和改进。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;#提问的智慧&lt;br&gt;
    
    </summary>
    
    
  </entry>
  
  <entry>
    <title>Vim 从入门到精通</title>
    <link href="https://www.07sec.com/2018/02/19/vim-galore/"/>
    <id>https://www.07sec.com/2018/02/19/vim-galore/</id>
    <published>2018-02-19T06:49:13.000Z</published>
    <updated>2022-04-03T03:27:57.000Z</updated>
    
    <content type="html"><![CDATA[<ul><li>原文地址：<a href="https://github.com/mhinz/vim-galore" target="_blank" rel="noopener">https://github.com/mhinz/vim-galore</a></li><li>原文作者：Marco Hinz</li><li>本文地址：<a href="https://github.com/wsdjeg/vim-galore-zh_cn" target="_blank" rel="noopener">https://github.com/wsdjeg/vim-galore-zh_cn</a><br><strong>注:转载请保留以上信息</strong><a id="more"></a><h1 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h1></li></ul><h2 id="什么是-Vim？"><a href="#什么是-Vim？" class="headerlink" title="什么是 Vim？"></a>什么是 Vim？</h2><p><a href="http://www.vim.org" target="_blank" rel="noopener">Vim</a> 是一个历史悠久的文本编辑器，可以追溯到 <a href="https://en.wikipedia.org/wiki/QED_(text_editor" target="_blank" rel="noopener">qed</a>)。<a href="https://en.wikipedia.org/wiki/Bram_Moolenaar" target="_blank" rel="noopener">Bram<br>Moolenaar</a> 于 1991 年发布初始版本。</p><p>该项目托管在 <a href="http://www.vim.org/index.php" target="_blank" rel="noopener">vim.org</a>。</p><p>获取 Vim：用包管理器安装或者直接到 vim.org <a href="http://www.vim.org/download.php" target="_blank" rel="noopener">下载</a>。</p><p>对于 Windows 用户，可以从 <a href="https://share.weiyun.com/da2be5937ac0e2bd3abc26355fad1204" target="_blank" rel="noopener">我的网盘</a> 下载。该版本可轻易添加 <code>python</code> 、<code>python3</code> 、<code>lua</code> 等支持，只需要安装 python、lua 即可。</p><p>讨论使用相关问题最好使用 <a href="https://groups.google.com/forum/#!forum/vim_use" target="_blank" rel="noopener">vim_use</a> 邮件列表或者使用 IRC(<a href="https://freenode.net" target="_blank" rel="noopener">Freenode</a>) 的 <code>#vim</code> 频道。</p><p>欢迎加入我们的中文讨论群：<a href="https://jq.qq.com/?_wv=1027&amp;k=43DB6SG" target="_blank" rel="noopener">QQ</a></p><p>项目在 <a href="https://github.com/vim/vim" target="_blank" rel="noopener">Github</a> 上开发，项目讨论请订阅 <a href="https://groups.google.com/forum/#!forum/vim_dev" target="_blank" rel="noopener">vim_dev</a> 邮件列表。</p><p>通过阅读 <a href="http://www.viemu.com/a-why-vi-vim.html" target="_blank" rel="noopener">Why, oh WHY, do those #?@! nutheads use vi?</a> 来对 Vim 进行大致的了解。</p><h2 id="Vim-哲学"><a href="#Vim-哲学" class="headerlink" title="Vim 哲学"></a>Vim 哲学</h2><p>Vim 采用模式编辑的理念，即它提供了多种模式，按键在不同的模式下作用不同。你可以在 <em>普通模式</em> 下浏览文件，在 <em>插入模式</em> 下插入文本，在 <em>可视模式</em> 下选择行，在 <em>命令模式</em> 下执行命令等等。起初这听起来可能很复杂，但是这有一个很大的优点：不需要通过同时按住多个键来完成操作，大多数时候你只需要依次按下这些按键即可。越常用的操作，所需要的按键数量越少。</p><p>和模式编辑紧密相连的概念是“操作符”和“动作”。<em>操作符</em> 开始一些行为，例如：修改，删除，或者选择文本。之后你要用一个 _动作_ 来指定需要操作的文本区域。比如，要改变括号内的文本，需要执行 <code>ci(</code> （读做 <em>change inner parentheses</em>）；删除整个段落的内容，需要执行 <code>dap</code> （读做：<em>delete<br>around paragraph</em>）。</p><p>如果你能看见 Vim 老司机操作，你会发现他们使用 Vim 脚本语言就如同钢琴师弹钢琴一样。复杂的操作只需要几个按键就能完成。他们甚至不用刻意去想，因为这已经成为<a href="https://en.wikipedia.org/wiki/Muscle_memory" target="_blank" rel="noopener">肌肉记忆</a>了。这减少<a href="https://en.wikipedia.org/wiki/Cognitive_load" target="_blank" rel="noopener">认识负荷</a>并帮助人们专注于实际任务。</p><h2 id="入门"><a href="#入门" class="headerlink" title="入门"></a>入门</h2><p>Vim 自带一个交互式的教程，内含你需要了解的最基础的信息，你可以通过终端运行以下命令打开教程：</p><pre><code>$ vimtutor</code></pre><p>不要因为这个看上去很无聊而跳过，按照此教程多练习。你以前用的 IDE 或者其他编辑器很少是有“模式”概念的，因此一开始你会很难适应模式切换。但是你 Vim 使用的越多，<a href="https://en.wikipedia.org/wiki/Muscle_memory" target="_blank" rel="noopener">肌肉记忆</a> 将越容易形成。</p><p>Vim 基于一个 <a href="https://en.wikipedia.org/wiki/Vi" target="_blank" rel="noopener">vi</a> 克隆，叫做 <a href="https://en.wikipedia.org/wiki/Stevie_(text_editor" target="_blank" rel="noopener">Stevie</a>)，支持两种运行模式：”compatible” 和 “nocompatible”。在兼容模式下运行 Vim 意味着使用 vi 的默认设置，而不是 Vim 的默认设置。除非你新建一个用户的 <code>vimrc</code> 或者使用 <code>vim -N</code> 命令启动 Vim，否则就是在兼容模式下运行 Vim！请大家不要在兼容模式下运行 Vim。</p><p>下一步</p><ol><li>创建你自己的 <a href="#精简的-vimrc">vimrc</a>。</li><li>在第一周准备<a href="#备忘录">备忘录</a>。</li><li>通读<a href="#基础-1">基础</a>章节了解 Vim 还有哪些功能。</li><li>按需学习！Vim 是学不完的。如果你遇到了问题，先上网寻找解决方案，你的问题可能已经被解决了。Vim 拥有大量的参考文档，知道如何利用这些参考文档很有必要：<a href="#获取离线帮助">获取离线帮助</a>。</li><li>浏览<a href="#附加资源">附加资源</a>。</li></ol><p>最后一个建议：使用<a href="#插件管理">插件</a>之前，请先掌握 Vim 的基本操作。很多插件都只是对 Vim 自带功能的封装。</p><h2 id="精简的-vimrc"><a href="#精简的-vimrc" class="headerlink" title="精简的 vimrc"></a>精简的 vimrc</h2><p>用户的 vimrc 配置文件可以放在 <code>~/.vimrc</code>，或者为了更好的分离放在 <code>~/.vim/vimrc</code>，后者更便于通过版本控制软件备份和同步整个配置，比方说 Github。</p><p>你可以在网上找到许多精简的 vimrc 配置文件，我的版本可能并不是最简单的版本，但是我的版本提供了一套我认为良好的，非常适合入门的设置。</p><p>最终你需要阅读完那些设置，然后自行决定需要使用哪些。:-)</p><p>精简的 vimrc 地址：<a href="contents/minimal-vimrc.vim">minimal-vimrc</a></p><p>如果你有兴趣，这里是我（原作者）的 <a href="https://github.com/mhinz/dotfiles/blob/master/vim/vimrc" target="_blank" rel="noopener">vimrc</a>。</p><p><strong>建议</strong>：大多数插件作者都维护不止一个插件并且将他们的 vimrc 放在 Github 上展示（通常放在叫做 “vim-config” 或者 “dotfiles” 的仓库中），所以当你发现你喜欢的插件时，去插件维护者的 Github 主页看看有没有这样的仓库。</p><h2 id="我正在使用什么样的-Vim"><a href="#我正在使用什么样的-Vim" class="headerlink" title="我正在使用什么样的 Vim"></a>我正在使用什么样的 Vim</h2><p>使用 <code>:version</code> 命令将向你展示当前正在运行的 Vim 的所有相关信息，包括它是如何编译的。</p><p>第一行告诉你这个二进制文件的编译时间和版本号，比如：7.4。接下来的一行呈现 <code>Included patches: 1-1051</code>，这是补丁版本包。因此你 Vim 确切的版本号是  7.4.1051。</p><p>另一行显示着一些像 <code>Tiny version without GUI</code> 或者 <code>Huge version with GUI</code> 的信息。很显然这些信息告诉你当前的 Vim 是否支持 GUI，例如：从终端中运行 <code>gvim</code> 或者从终端模拟器中的 Vim 内运行 <code>:gui</code> 命令。另一个重要的信息是 <code>Tiny</code> 和 <code>Huge</code>。Vim 的特性集区分被叫做 <code>tiny</code>，<code>small</code>，<code>normal</code>，<code>big</code> and <code>huge</code>，所有的都实现不同的功能子集。</p><p><code>:version</code> 主要的输出内容是特性列表。<code>+clipboard</code> 意味这剪贴板功能被编译支持了，<code>-clipboard</code> 意味着剪贴板特性没有被编译支持。</p><p>一些功能特性需要编译支持才能正常工作。例如：为了让 <code>:prof</code> 工作，你需要使用 <code>huge</code> 模式编译的 Vim，因为那种模式启用了 <code>+profile</code> 特性。</p><p>如果你的输出情况并不是那样，并且你是从包管理器安装 Vim 的，确保你安装了 <code>vim-x</code>，<code>vim-x11</code>，<code>vim-gtk</code>，<code>vim-gnome</code> 这些包或者相似的，因为这些包通常都是 <code>huge</code> 模式编译的。</p><p>你也可以运行下面这段代码来测试 Vim 版本以及功能支持：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">" Do something if running at least Vim 7.4.42 with +profile enabled.</span></span><br><span class="line"><span class="keyword">if</span> (<span class="variable">v:version</span> &gt; <span class="number">704</span> || <span class="variable">v:version</span> == <span class="number">704</span> &amp;&amp; <span class="built_in">has</span>(<span class="string">'patch42'</span>)) &amp;&amp; <span class="built_in">has</span>(<span class="string">'profile'</span>)</span><br><span class="line">  <span class="comment">" do stuff</span></span><br><span class="line"><span class="keyword">endif</span></span><br></pre></td></tr></table></figure><p>相关帮助：</p><pre><code>:h :version:h feature-list:h +feature-list:h has-patch</code></pre><h2 id="备忘录"><a href="#备忘录" class="headerlink" title="备忘录"></a>备忘录</h2><p>为了避免版权问题，我只贴出链接：</p><ul><li><a href="http://people.csail.mit.edu/vgod/vim/vim-cheat-sheet-en.png" target="_blank" rel="noopener">http://people.csail.mit.edu/vgod/vim/vim-cheat-sheet-en.png</a></li><li><a href="https://cdn.shopify.com/s/files/1/0165/4168/files/preview.png" target="_blank" rel="noopener">https://cdn.shopify.com/s/files/1/0165/4168/files/preview.png</a></li><li><a href="http://www.nathael.org/Data/vi-vim-cheat-sheet.svg" target="_blank" rel="noopener">http://www.nathael.org/Data/vi-vim-cheat-sheet.svg</a></li><li><a href="http://michael.peopleofhonoronly.com/vim/vim_cheat_sheet_for_programmers_screen.png" target="_blank" rel="noopener">http://michael.peopleofhonoronly.com/vim/vim_cheat_sheet_for_programmers_screen.png</a></li><li><a href="http://www.rosipov.com/images/posts/vim-movement-commands-cheatsheet.png" target="_blank" rel="noopener">http://www.rosipov.com/images/posts/vim-movement-commands-cheatsheet.png</a></li></ul><p>或者在 Vim 中快速打开备忘录：<a href="https://github.com/lifepillar/vim-cheat40" target="_blank" rel="noopener">vim-cheat40</a>。</p><h1 id="基础"><a href="#基础" class="headerlink" title="基础"></a>基础</h1><h2 id="缓冲区，窗口，标签"><a href="#缓冲区，窗口，标签" class="headerlink" title="缓冲区，窗口，标签"></a>缓冲区，窗口，标签</h2><p>Vim 是一个文本编辑器。每次文本都是作为<strong>缓冲区</strong>的一部分显示的。每一份文件都是在他们自己独有的缓冲区打开的，插件显示的内容也在它们自己的缓冲区中。</p><p>缓冲区有很多属性，比如这个缓冲区的内容是否可以修改，或者这个缓冲区是否和文件相关联，是否需要同步保存到磁盘上。</p><p><strong>窗口</strong> 是缓冲区上一层的视窗。如果你想同时查看几个文件或者查看同一文件的不同位置，那样你会需要窗口。</p><p>请别把他们叫做 _分屏_ 。你可以把一个窗口分割成两个，但是这并没有让这两个窗口完全 _分离_ 。</p><p>窗口可以水平或者竖直分割并且现有窗口的高度和宽度都是可以被调节设置的，因此，如果你需要多种窗口布局，请考虑使用标签。</p><p><strong>标签页</strong> （标签）是窗口的集合。因此当你想使用多种窗口布局时候请使用标签。</p><p>简单的说，如果你启动 Vim 的时候没有附带任何参数，你会得到一个包含着一个呈现一个缓冲区的窗口的标签。</p><p>顺带提一下，缓冲区列表是全局可见的，你可以在任何标签中访问任何一个缓冲区。</p><h2 id="已激活、已载入、已列出、已命名的缓冲区"><a href="#已激活、已载入、已列出、已命名的缓冲区" class="headerlink" title="已激活、已载入、已列出、已命名的缓冲区"></a>已激活、已载入、已列出、已命名的缓冲区</h2><p>用类似 <code>vim file1</code> 的命令启动 Vim 。这个文件的内容将会被加载到缓冲区中，你现在有一个<strong>已载入的缓冲区</strong>。如果你在 Vim 中保存这个文件，缓冲区内容将会被同步到磁盘上（写回文件中）。</p><p>由于这个缓冲区也在一个窗口上显示，所以他也是一个<strong>已激活的缓冲区</strong>。如果你现在通过 <code>:e file2</code> 命令加载另一个文件，<code>file1</code> 将会变成一个<strong>隐藏的缓冲区</strong>，并且 <code>file2</code> 变成已激活缓冲区。</p><p>使用 <code>:ls</code> 我们能够列出所有可以列出的缓冲区。插件缓冲区和帮助缓冲区通常被标记为不可以列出的缓冲区，因为那并不是你经常需要在编辑器中编辑的常规文件。通过 <code>:ls!</code> 命令可以显示被放入缓冲区列表的和未被放入列表的缓冲区。</p><p><strong>未命名的缓冲区</strong>是一种没有关联特定文件的缓冲区，这种缓冲区经常被插件使用。比如 <code>:enew</code> 将会创建一个无名临时缓冲区。添加一些文本然后使用 <code>:w /tmp/foo</code> 将他写入到磁盘，这样这个缓冲区就会变成一个<strong>已命名的缓冲区</strong>。</p><h2 id="参数列表"><a href="#参数列表" class="headerlink" title="参数列表"></a>参数列表</h2><p><a href="#缓冲区窗口标签">全局缓冲区列表</a>是 Vim 的特性。在这之前的 vi 中，仅仅只有参数列表，参数列表在 Vim 中依旧可以使用。</p><p>每一个通过 shell 命令传递给 Vim 的文件名都被记录在一个参数列表中。可以有多个参数列表：默认情况下所有参数都被放在全局参数列表下，但是你可以使用 <code>:arglocal</code> 命令去创建一个新的本地窗口的参数列表。</p><p>使用 <code>:args</code> 命令可以列出当前参数。使用 <code>:next</code>，<code>:previous</code>，<code>:first</code>，<code>:last</code> 命令可以在切换在参数列表中的文件。通过使用 <code>:argadd</code>，<code>:argdelete</code> 或者 <code>:args</code> 等命令加上一个文件列表可以改变参数列表。</p><p>偏爱缓冲区列表还是参数列表完全是个人选择，我的印象中大多数人都是使用缓冲区列表的。</p><p>然而参数列表在有些情况下被大量使用：批处理<br>使用 <code>:argdo</code>！ 一个简单的重构例子：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">:<span class="keyword">args</span> **/*.[ch]</span><br><span class="line">:<span class="keyword">argdo</span> %s/foo/bar/ge | <span class="keyword">update</span></span><br></pre></td></tr></table></figure><p>这条命令将替换掉当前目录下以及当前目录的子目录中所有的 C 源文件和头文件中的“foo”，并用“bar”代替。</p><p>相关帮助：<code>:h argument-list</code></p><h2 id="按键映射"><a href="#按键映射" class="headerlink" title="按键映射"></a>按键映射</h2><p>使用 <code>:map</code> 命令家族你可以定义属于你自己的快捷键。该家族的每一个命令都限定在特定的模式下。从技术上来说 Vim 自带高达 12 中模式，其中 6 种可以被映射。另外一些命令作用于多种模式：</p><table><thead><tr><th>递归</th><th>非递归</th><th>模式</th></tr></thead><tbody><tr><td><code>:map</code></td><td><code>:noremap</code></td><td>normal, visual, operator-pending</td></tr><tr><td><code>:nmap</code></td><td><code>:nnoremap</code></td><td>normal</td></tr><tr><td><code>:xmap</code></td><td><code>:xnoremap</code></td><td>visual</td></tr><tr><td><code>:cmap</code></td><td><code>:cnoremap</code></td><td>command-line</td></tr><tr><td><code>:omap</code></td><td><code>:onoremap</code></td><td>operator-pending</td></tr><tr><td><code>:imap</code></td><td><code>:inoremap</code></td><td>insert</td></tr></tbody></table><p>例如：这个自定义的快捷键只在普通模式下工作。</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:<span class="keyword">nmap</span> <span class="symbol">&lt;space&gt;</span> :<span class="keyword">echo</span> <span class="string">"foo"</span><span class="symbol">&lt;cr&gt;</span></span><br></pre></td></tr></table></figure><p>使用 <code>:nunmap &lt;space&gt;</code> 可以取消这个映射。</p><p>对于更少数，不常见的模式（或者他们的组合），查看 <code>:h map-modes</code>。</p><p>到现在为止还好，对新手而言有一个问题会困扰他们：<code>:nmap</code> 是<strong>递归执行</strong>的！结果是，右边执行可能的映射。</p><p>你自定义了一个简单的映射去输出“Foo”：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:<span class="keyword">nmap</span> <span class="keyword">b</span> :<span class="keyword">echo</span> <span class="string">"Foo"</span><span class="symbol">&lt;cr&gt;</span></span><br></pre></td></tr></table></figure><p>但是如果你想要映射 <code>b</code> （回退一个单词）的默认功能到一个键上呢？</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:<span class="keyword">nmap</span> <span class="keyword">a</span> <span class="keyword">b</span></span><br></pre></td></tr></table></figure><p>如果你敲击<kbd>a</kbd>，我们期望着光标回退到上一个单词，但是实际情况是“Foo”被输出到命令行里！因为在右边，<code>b</code> 已经被映射到别的行为上了，换句话说就是 <code>:echo &quot;Foo&quot;&lt;cr&gt;</code>。</p><p>解决此问题的正确方法是使用一种 <em>非递归</em> 的映射代替：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:<span class="keyword">nnoremap</span> <span class="keyword">a</span> <span class="keyword">b</span></span><br></pre></td></tr></table></figure><p>经验法则：除非递归是必须的，否则总是使用非递归映射。</p><p>通过不给一个右值来检查你的映射。比如<code>:nmap</code> 显示所以普通模式下的映射，<code>:nmap &lt;leader&gt;</code> 显示所有以 <code>&lt;leader&gt;</code> 键开头的普通模式下的映射。</p><p>如果你想禁止用标准映射，把他们映射到特殊字符 <code>&lt;nop&gt;</code> 上，例如：<code>:noremap &lt;left&gt; &lt;nop&gt;</code>。</p><p>相关帮助：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">:h key-notation</span><br><span class="line">:h mapping</span><br><span class="line">:h <span class="number">05.3</span></span><br></pre></td></tr></table></figure><h2 id="映射前置键"><a href="#映射前置键" class="headerlink" title="映射前置键"></a>映射前置键</h2><p>映射前置键（Leader 键）本身就是一个按键映射，默认为 <kbd>\</kbd>。我们可以通过在 <code>map</code> 中调用 <code>&lt;leader&gt;</code> 来为把它添加到其他按键映射中。</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">nnoremap</span> <span class="symbol">&lt;leader&gt;</span>h :<span class="keyword">helpgrep</span><span class="symbol">&lt;space&gt;</span></span><br></pre></td></tr></table></figure><p>这样，我们只需要先按 <kbd>\</kbd> 然后连续按 <kbd>\h</kbd> 就可以激活这个映射 <code>:helpgrep&lt;space&gt;</code>。如果你想通过先按 <kbd>空格</kbd> 键来触发，只需要这样做：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> mapleader = <span class="string">' '</span></span><br><span class="line"><span class="keyword">nnoremap</span> <span class="symbol">&lt;leader&gt;</span>h :<span class="keyword">helpgrep</span><span class="symbol">&lt;space&gt;</span></span><br></pre></td></tr></table></figure><p>另外，还有一个叫 <code>&lt;localleader&gt;</code> 的，可以把它理解为局部环境中的 <code>&lt;leader&gt;</code>，默认值依然为 <kbd>\</kbd>。当我们需要只对某一个条件下（比如，特定文件类型的插件）的缓冲区设置特别的 <code>&lt;leader&gt;</code> 键，那么我们就可以通过修改当前环境下的 <code>&lt;localleader&gt;</code> 来实现。</p><p><strong>注意</strong>：如果你打算设置 Leader 键，请确保在设置按键映射之前，先设置好 Leader 键。如果你先设置了含有 Leader 键的映射，然后又修改了 Leader 键，那么之前映射内的 Leader 键是不会因此而改变的。你可以通过执行 <code>:nmap &lt;leader&gt;</code> 来查看普通模式中已绑定给 Leader 键的所有映射。</p><p>请参阅 <code>:h mapleader</code> 与 <code>:h maploacalleader</code> 来获取更多帮助。</p><h2 id="寄存器"><a href="#寄存器" class="headerlink" title="寄存器"></a>寄存器</h2><p>寄存器就是存储文本的地方。我们常用的「复制」操作就是把文本存储到寄存器，「 粘贴」 操作就是把文本从寄存器中读出来。顺便，在 Vim 中复制的快捷键是 <kbd>y</kbd>，粘贴的快捷键是 <kbd>p</kbd>。</p><p>Vim 为我们提供了如下的寄存器：</p><table><thead><tr><th>类型</th><th>标识</th><th>读写者</th><th>是否为只读</th><th>包含的字符来源</th></tr></thead><tbody><tr><td>Unnamed</td><td><code>&quot;</code></td><td>vim</td><td>否</td><td>最近一次的复制或删除操作 (<code>d</code>, <code>c</code>, <code>s</code>, <code>x</code>, <code>y</code>)</td></tr><tr><td>Numbered</td><td><code>0</code>至<code>9</code></td><td>vim</td><td>否</td><td>寄存器 <code>0</code>: 最近一次复制。寄存器 <code>1</code>: 最近一次删除。寄存器 <code>2</code>: 倒数第二次删除，以此类推。对于寄存器 <code>1</code> 至 <code>9</code>，他们其实是只读的最多包含 9 个元素的队列。这里的队列即为数据类型 <a href="https://en.wikipedia.org/wiki/Queue_(abstract_data_type" target="_blank" rel="noopener">queue</a>)</td></tr><tr><td>Small delete</td><td><code>-</code></td><td>vim</td><td>否</td><td>最近一次行内删除</td></tr><tr><td>Named</td><td><code>a</code>至<code>z</code>, <code>A</code>至<code>Z</code></td><td>用户</td><td>否</td><td>如果你通过复制操作存储文本至寄存器 <code>a</code>，那么 <code>a</code> 中的文本就会被完全覆盖。如果你存储至 <code>A</code>，那么会将文本添加给寄存器 <code>a</code>，不会覆盖之前已有的文本</td></tr><tr><td>Read-only</td><td><code>:</code>与<code>.</code>和<code>%</code></td><td>vim</td><td>是</td><td><code>:</code>: 最近一次使用的命令，<code>.</code>: 最近一次添加的文本，<code>%</code>: 当前的文件名</td></tr><tr><td>Alternate buffer</td><td><code>#</code></td><td>vim</td><td>否</td><td>大部分情况下，这个寄存器是当前窗口中，上一次访问的缓冲区。请参阅 <code>:h alternate-file</code> 来获取更多帮助</td></tr><tr><td>Expression</td><td><code>=</code></td><td>用户</td><td>否</td><td>复制 VimL 代码时，这个寄存器用于存储代码片段的执行结果。比如，在插入模式下复制 <code>&lt;c-r&gt;=5+5&lt;cr&gt;</code>，那么这个寄存器就会存入 10</td></tr><tr><td>Selection</td><td><code>+</code>和<code>*</code></td><td>vim</td><td>否</td><td><code>*</code> 和 <code>+</code> 是 <a href="#剪贴板">剪贴板</a> 寄存器</td></tr><tr><td>Drop</td><td><code>~</code></td><td>vim</td><td>是</td><td>最后一次拖拽添加至 Vim 的文本（需要 “+dnd” 支持，暂时只支持 GTK GUI。请参阅 <code>:help dnd</code> 及 <code>:help quote~</code>）</td></tr><tr><td>Black hole</td><td><code>_</code></td><td>vim</td><td>否</td><td>一般称为黑洞寄存器。对于当前操作，如果你不希望在其他寄存器中保留文本，那就在命令前加上 <code>_</code>。比如，<code>&quot;_dd</code> 命令不会将文本放到寄存器 <code>&quot;</code>、<code>1</code>、<code>+</code> 或 <code>*</code> 中</td></tr><tr><td>Last search pattern</td><td><code>/</code></td><td>vim</td><td>否</td><td>最近一次通过 <code>/</code>、<code>?</code> 或 <code>:global</code> 等命令调用的匹配条件</td></tr></tbody></table><p>只要不是只读的寄存器，用户都有权限修改它的内容，比如：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:<span class="keyword">let</span> @/ = <span class="string">'register'</span></span><br></pre></td></tr></table></figure><p>这样，我们按 <kbd>n</kbd> 的时候就会跳转到单词”register” 出现的地方。</p><p>有些时候，你的操作可能已经修改了寄存器，而你没有察觉到。请参阅 <code>:h registers</code> 获取更多帮助。</p><p>上面提到过，复制的命令是 <kbd>y</kbd>，粘贴的命令是 <kbd>p</kbd> 或者 <kbd>P</kbd>。但请注意，Vim 会区分「字符选取」与「行选取」。请参阅 <code>:h linewise</code> 获取更多帮助。</p><p><strong>行选取</strong>：<br>命令 <code>yy</code> 或 <code>Y</code> 都是复制当前行。这时移动光标至其他位置，按下 <code>p</code> 就可以在光标下方粘贴复制的行，按下 <code>P</code> 就可以在光标上方粘贴至复制的行。</p><p><strong>字符选取</strong>：<br>命令 <code>0yw</code> 可以复制第一个单词。这时移动光标至其他位置，按下 <code>p</code> 就可以在当前行、光标后的位置粘贴单词，按下 <code>P</code> 就可以在当前行、光标前的位置粘贴单词。</p><p><strong>将文本存到指定的寄存器中</strong>：<br>命令 <code>&quot;aY</code> 可以将当前行复制，并存储到寄存器 <code>a</code> 中。这时移动光标至其他位置，通过命令 <code>&quot;AY</code> 就可以把这一行的内容扩展到寄存器 <code>a</code> 中，而之前存储的内容也不会丢失。</p><p>为了便于理解和记忆，建议大家现在就试一试上面提到的这些操作。操作过程中，你可以随时通过 <code>:reg</code> 来查看寄存器的变化。</p><p><strong>有趣的是</strong>：<br>在 Vim 中，<code>y</code> 是复制命令，源于单词 “yanking”。而在 Emacs 中，”yanking” 代表的是粘贴（或者说，重新插入刚才删掉的内容），而并不是复制。</p><h2 id="范围"><a href="#范围" class="headerlink" title="范围"></a>范围</h2><p>范围 (Ranges) 其实很好理解，但很多 Vim 用户的理解不到位。</p><ul><li>很多命令都可以加一个数字，用于指明操作范围</li><li>范围可以是一个行号，用于指定某一行</li><li>范围也可以是一对通过 <code>,</code> 或 <code>;</code> 分割的行号</li><li>大部分命令，默认只作用于当前行</li><li>只有 <code>:write</code> 和 <code>:global</code> 是默认作用于所有行的</li></ul><p>范围的使用是十分直观的。以下为一些例子（其中，<code>:d</code> 为 <code>:delete</code> 的缩写）：</p><table><thead><tr><th>命令</th><th>操作的行</th></tr></thead><tbody><tr><td><code>:d</code></td><td>当前行</td></tr><tr><td><code>:.d</code></td><td>当前行</td></tr><tr><td><code>:1d</code></td><td>第一行</td></tr><tr><td><code>:$d</code></td><td>最后一行</td></tr><tr><td><code>:1,$d</code></td><td>所有行</td></tr><tr><td><code>:%d</code></td><td>所有行（这是 <code>1,$</code> 的语法糖）</td></tr><tr><td><code>:.,5d</code></td><td>当前行至第 5 行</td></tr><tr><td><code>:,5d</code></td><td>同样是当前行至第 5 行</td></tr><tr><td><code>:,+3d</code></td><td>当前行及接下来的 3 行</td></tr><tr><td><code>:1,+3d</code></td><td>第一行至当前行再加 3 行</td></tr><tr><td><code>:,-3d</code></td><td>当前行及向上的 3 行（Vim 会弹出提示信息，因为这是一个保留的范围）</td></tr><tr><td><code>:3,&#39;xdelete</code></td><td>第三行至<a href="#标注">标注</a> 为 x 的那一行</td></tr><tr><td><code>:/^foo/,$delete</code></td><td>当前行以下，以字符 “foo” 开头的那一行至结尾</td></tr><tr><td><code>:/^foo/+1,$delete</code></td><td>当前行以下，以字符 “foo” 开头的那一行的下一行至结尾</td></tr></tbody></table><p>需要注意的是，<code>;</code> 也可以用于表示范围。区别在于，<code>a,b</code> 的 <code>b</code> 是以当前行作为参考的。而 <code>a;b</code> 的 <code>b</code> 是以 <code>a</code> 行作为参考的。举个例子，现在你的光标在第 5 行。这时 <code>:1,+1d</code> 会删除第 1 行至第 6 行，而 <code>:1;+1d</code> 会删除第 1 行和第 2 行。</p><p>如果你想设置多个寻找条件，只需要在条件前加上 <code>/</code>，比如：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:/foo//bar//quux/d</span><br></pre></td></tr></table></figure><p>这就会删除当前行之后的某一行。定位方式是，先在当前行之后寻找第一个包含 “foo” 字符的那一行，然后在找到的这一行之后寻找第一个包含 “bar” 字符的那一行，然后再在找到的这一行之后寻找第一个包含 “quux” 的那一行。删除的就是最后找到的这一行。</p><p>有时，Vim 会在命令前自动添加范围。举个例子，如果你先通过 <code>V</code> 命令进入行选取模式，选中一些行后按下 <code>:</code> 进入命令模式，这时候你会发现 Vim 自动添加了 <code>&#39;&lt;,&#39;&gt;</code> 范围。这表示，接下来的命令会使用之前选取的行号作为范围。但如果后续命令不支持范围，Vim 就会报错。为了避免这样的情况发生，有些人会设置这样的按键映射：<code>:vnoremap foo :&lt;c-u&gt;command</code>，组合键 <kbd>Ctrl + u</kbd> 可以清除当前命令行中的内容。</p><p>另一个例子是在普通模式中按下 <code>!!</code>，命令行中会出现 <code>:.!</code>。如果这时你如果输入一个外部命令，那么当前行的内容就会被这个外部命令的输出替换。你也可以通过命令 <code>:?^$?+1,/^$/-1!ls</code> 把当前段落的内容替换成外部命令 <code>ls</code> 的输出，原理是向前和向后各搜索一个空白行，删除这两个空白行之间的内容，并将外部命令 <code>ls</code> 的输出放到这两个空白行之间。</p><p>请参阅以下两个命令来获取更多帮助：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">:h cmdline-ranges</span><br><span class="line">:h <span class="number">10.3</span></span><br></pre></td></tr></table></figure><h2 id="标注"><a href="#标注" class="headerlink" title="标注"></a>标注</h2><p>你可以使用标注功能来标记一个位置，也就是记录文件某行的某个位置。</p><table><thead><tr><th>标注</th><th>设置者</th><th>使用</th></tr></thead><tbody><tr><td><code>a</code>-<code>z</code></td><td>用户</td><td>仅对当前的一个文件生效，也就意味着只可以在当前文件中跳转</td></tr><tr><td><code>A</code>-<code>Z</code></td><td>用户</td><td>全局标注，可以作用于不同文件。大写标注也称为「文件标注」。跳转时有可能会切换到另一个缓冲区</td></tr><tr><td><code>0</code>-<code>9</code></td><td>viminfo</td><td><code>0</code> 代表 viminfo 最后一次被写入的位置。实际使用中，就代表 Vim 进程最后一次结束的位置。<code>1</code> 代表 Vim 进程倒数第二次结束的位置，以此类推</td></tr></tbody></table><p>如果想跳转到指定的标注，你可以先按下 <code>&#39;</code> / <code>g&#39;</code> 或者 <code>`</code> / <code>g`</code> 然后按下标注名。</p><p>如果你想定义当前文件中的标注，可以先按下 <code>m</code> 再按下标注名。比如，按下 <code>mm</code> 就可以把当前位置标注为 <code>m</code>。在这之后，如果你的光标切换到了文件的其他位置，只需要通过 <code>&#39;m</code> 或者 <code>\</code>m<code>即可回到刚才标注的行。区别在于，</code>‘m<code>会跳转回被标记行的第一个非空字符，而</code>`m<code>会跳转回被标记行的被标记列。根据 viminfo 的设置，你可以在退出 Vim 的时候保留小写字符标注。请参阅</code>:h viminfo-‘` 来获取更多帮助。</p><p>如果你想定义全局的标注，可以先按下 <code>m</code> 再按下大写英文字符。比如，按下 <code>mM</code> 就可以把当前文件的当前位置标注为 <code>M</code>。在这之后，就算你切换到其他的缓冲区，依然可以通过 <code>&#39;M</code> 或 <code>\</code>M` 跳转回来。</p><p>关于跳转，还有以下的方式：</p><table><thead><tr><th>按键</th><th>跳转至</th></tr></thead><tbody><tr><td><code>&#39;[</code> 与 <code>`[</code></td><td>上一次修改或复制的第一行或第一个字符</td></tr><tr><td><code>&#39;]</code> 与 <code>`]</code></td><td>上一次修改或复制的最后一行或最后一个字符</td></tr><tr><td><code>&#39;&lt;</code> 与 <code>`&lt;</code></td><td>上一次在可视模式下选取的第一行或第一个字符</td></tr><tr><td><code>&#39;&gt;</code> 与 <code>`&gt;</code></td><td>上一次在可视模式下选取的最后一行或最后一个字符</td></tr><tr><td><code>&#39;&#39;</code> 与 <code>`&#39;</code></td><td>上一次跳转之前的光标位置</td></tr><tr><td><code>&#39;&quot;</code> 与 <code>`&quot;</code></td><td>上一次关闭当前缓冲区时的光标位置</td></tr><tr><td><code>&#39;^</code> 与 <code>`^</code></td><td>上一次插入字符后的光标位置</td></tr><tr><td><code>&#39;.</code> 与 <code>`.</code></td><td>上一次修改文本后的光标位置</td></tr><tr><td><code>&#39;(</code> 与 <code>`(</code></td><td>当前句子的开头</td></tr><tr><td><code>&#39;)</code> 与 <code>`)</code></td><td>当前句子的结尾</td></tr><tr><td><code>&#39;{</code> 与 <code>`{</code></td><td>当前段落的开头</td></tr><tr><td><code>&#39;}</code> 与 <code>`}</code></td><td>当前段落的结尾</td></tr></tbody></table><p>标注也可以搭配 <a href="#范围">范围</a> 一起使用。前面提到过，如果你在可视模式下选取一些文本，然后按下 <code>:</code>，这时候你会发现命令行已经被填充了 <code>:&#39;&lt;,&#39;&gt;</code>。对照上面的表格，现在你应该明白了，这段代表的就是可视模式下选取的范围。</p><p>请使用 <code>:marks</code> 命令来显示所有的标注，参阅 <code>:h mark-motions</code> 来获取关于标注的更多帮助。</p><h2 id="补全"><a href="#补全" class="headerlink" title="补全"></a>补全</h2><p>Vim 在插入模式中为我们提供了多种补全方案。如果有多个补全结果，Vim 会弹出一个菜单供你选择。</p><p>常见的补全有标签、项目中引入的模块或库中的方法名、文件名、字典及当前缓冲区的字段。</p><p>针对不同的补全方案，Vim 为我们提供了不同的按键映射。这些映射都是在<strong>插入模式中</strong>通过 <kbd>Ctrl</kbd> + <kbd>x</kbd> 来触发：</p><table><thead><tr><th>映射</th><th>类型</th><th>帮助文档</th></tr></thead><tbody><tr><td><code>&lt;c-x&gt;&lt;c-l&gt;</code></td><td>整行</td><td><code>:h i^x^l</code></td></tr><tr><td><code>&lt;c-x&gt;&lt;c-n&gt;</code></td><td>当前缓冲区中的关键字</td><td><code>:h i^x^n</code></td></tr><tr><td><code>&lt;c-x&gt;&lt;c-k&gt;</code></td><td>字典（请参阅 <code>:h &#39;dictionary&#39;</code>）中的关键字</td><td><code>:h i^x^k</code></td></tr><tr><td><code>&lt;c-x&gt;&lt;c-t&gt;</code></td><td>同义词字典（请参阅 <code>:h &#39;thesaurus&#39;</code>）中的关键字</td><td><code>:h i^x^t</code></td></tr><tr><td><code>&lt;c-x&gt;&lt;c-i&gt;</code></td><td>当前文件以及包含的文件中的关键字</td><td><code>:h i^x^i</code></td></tr><tr><td><code>&lt;c-x&gt;&lt;c-]&gt;</code></td><td>标签</td><td><code>:h i^x^]</code></td></tr><tr><td><code>&lt;c-x&gt;&lt;c-f&gt;</code></td><td>文件名</td><td><code>:h i^x^f</code></td></tr><tr><td><code>&lt;c-x&gt;&lt;c-d&gt;</code></td><td>定义或宏定义</td><td><code>:h i^x^d</code></td></tr><tr><td><code>&lt;c-x&gt;&lt;c-v&gt;</code></td><td>Vim 命令</td><td><code>:h i^x^v</code></td></tr><tr><td><code>&lt;c-x&gt;&lt;c-u&gt;</code></td><td>用户自定义补全（通过 <code>&#39;completefunc&#39;</code> 定义）</td><td><code>:h i^x^u</code></td></tr><tr><td><code>&lt;c-x&gt;&lt;c-o&gt;</code></td><td>Omni Completion（通过 <code>&#39;omnifunc&#39;</code> 定义）</td><td><code>:h i^x^o</code></td></tr><tr><td><code>&lt;c-x&gt;s</code></td><td>拼写建议</td><td><code>:h i^Xs</code></td></tr></tbody></table><p>尽管用户自定义补全与 Omni Completion 是不同的，但他们做的事情基本一致。共同点在于，他们都是一个监听当前光标位置的函数，返回值为一系列的补全建议。用户自定义补全是由用户定义的，基于用户的个人用途，因此你可以根据自己的喜好和需求随意定制。而 Omni Completion 是针对文件类型的补全，比如在 C 语言中补全一个结构体（struct）的成员（members），或者补全一个类的方法，因而它通常都是由文件类型插件设置和调用的。</p><p>如果你设置了 <code>&#39;complete&#39;</code> 选项，那么你就可以在一次操作中采用多种补全方案。这个选项默认包含了多种可能性，因此请按照自己的需求来配置。你可以通过 <code>&lt;c-n&gt;</code> 来调用下一个补全建议，或通过 <code>&lt;c-p&gt;</code> 来调用上一个补全建议。当然，这两个映射同样可以直接调用补全函数。请参阅 <code>:h i^n</code> 与 <code>:h &#39;complete&#39;</code> 来获得更多帮助。</p><p>如果你想配置弹出菜单的行为，请一定要看一看 <code>:h &#39;completeopt&#39;</code> 这篇帮助文档。默认的配置已经不错了，但我个人（原作者）更倾向于把 “noselect” 加上。</p><p>请参阅以下文档获取更多帮助：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">:h ins-completion</span><br><span class="line">:h popupmenu-<span class="built_in">keys</span></span><br><span class="line">:h <span class="keyword">new</span>-omni-completion</span><br></pre></td></tr></table></figure><h2 id="动作，操作符，文本对象"><a href="#动作，操作符，文本对象" class="headerlink" title="动作，操作符，文本对象"></a>动作，操作符，文本对象</h2><p><strong>动作</strong>也就是指移动光标的操作，你肯定很熟悉 <code>h</code>、<code>j</code>、<code>k</code> 和 <code>l</code>，以及 <code>w</code> 和 <code>b</code>。但其实，<code>/</code> 也是一个动作。他们都可以搭配数字使用，比如 <code>2?the&lt;cr&gt;</code> 可以将光标移动到倒数第二个 “the” 出现的位置。</p><p>以下会列出一些常用的动作。你也可以通过 <code>:h navigation</code> 来获取更多的帮助。</p><p><strong>操作符</strong>是对某个区域文本执行的操作。比如，<code>d</code>、<code>~</code>、<code>gU</code> 和 <code>&gt;</code> 都是操作符。这些操作符既可以在普通模式下使用，也可以在可视模式下使用。在普通模式中，顺序是先按操作符，再按动作指令，比如 <code>&gt;j</code>。在可是模式中，选中区域后直接按操作符就可以，比如 <code>Vjd</code>。</p><p>与动作一样，操作符也可以搭配数字使用，比如 <code>2gUw</code> 可以将当前单词以及下一个单词转成大写。由于动作和操作符都可以搭配数字使用，因此 <code>2gU2w</code> 与执行两次 <code>gU2w</code> 效果是相同的。</p><p>请参阅 <code>:h operator</code> 来查看所有的操作符。你也可以通过 <code>:set tildeop</code> 命令把 <code>~</code> 也变成一个操作符</p><p>值得注意的是，动作是单向的，而<strong>文本对象</strong>是双向的。文本对象不仅作用于符号（比如括号、中括号和大括号等）标记的范围内，也作用于整个单词、整个句子等其他情况。</p><p>文本对象不能用于普通模式中移动光标的操作，因为光标还没有智能到可以向两个方向同时跳转。但这个功能可以在可视模式中实现，因为在对象的一端选中的情况下，光标只需要跳转到另一端就可以了。</p><p>文本对象操作一般用 <code>i</code> 或 <code>a</code> 加上对象标识符操作，其中 <code>i</code> 表示在对象内（英文 inner）操作，<code>a</code> 表示对整个对象（英文 around）操作，这时开头和结尾的空格都会被考虑进来。举个例子，<code>diw</code> 可以删除当前单词，<code>ci(</code> 可以改变括号中的内容。</p><p>文本对象同样可以与数字搭配使用。比如，像 <code>(((  )))</code> 这样的文本，假如光标位于最内层的括号上或最内层的括号内，那么 <code>d2a(</code> 将会删除从最内层开始的两对括号，以及他们之间的所有内容。其实，<code>d2a(</code> 这个操作等同于 <code>2da(</code>。在 Vim 的命令中，如果有两处都可以接收数字作为参数，那么最终结果就等同于两个数字相乘。在这里，<code>d</code> 与 <code>a(</code> 都是可以接收参数的，一个参数是 1，另一个是 2，我们可以把它们相乘然后放到最前面。</p><p>请参阅 <code>:h text-objects</code> 来获取更多关于文本对象的帮助。</p><h2 id="自动命令"><a href="#自动命令" class="headerlink" title="自动命令"></a>自动命令</h2><p>在特定的情况下，Vim 会传出事件。如果你想针对这些事件执行回调方法，那么就需要用到自动命令这个功能。</p><p>如果没有了自动命令，那你基本上是用不了 Vim 的。自动命令一直都在执行，只是很多时候你没有注意到。不信的话，可以执行命令 <code>:au</code> ，不要被结果吓到，这些是当前有效的所有自动命令。</p><p>请使用 <code>:h {event}</code> 来查看 Vim 中所有事件的列表，你也可以参考 <code>:h autocmd-events-abc</code> 来获取关于事件的更多帮助。</p><p>一个很常用的例子，就是针对文件类型执行某些设置：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">autocmd</span> FileType <span class="keyword">ruby</span> <span class="keyword">setlocal</span> <span class="built_in">shiftwidth</span>=<span class="number">2</span> softtabstop=<span class="number">2</span> comments-=:#</span><br></pre></td></tr></table></figure><p>但是缓冲区是如何知道当前的文件中包含 Ruby 代码呢？这其实是另一个自动命令检测的到的，然后把文件类型设置成为 Ruby，这样就触发了上面的 <code>FileType</code> 事件。</p><p>在配置 vimrc 的时候，一般第一行加进去的就是 <code>filetype on</code>。这就意味着，Vim 启动时会读取 <code>filetype.vim</code> 文件，然后根据文件类型来触发相应的自动命令。</p><p>如果你勇于尝试，可以查看下 <code>:e $VIMRUNTIME/filetype.vim</code>，然后在输出中搜索 “Ruby”。这样，你就会发现其实 Vim 只是通过文件扩展名 <code>.rb</code> 判断某个文件是不是 Ruby 的。</p><p><strong>注意</strong>：对于相同事件，如果有多个自动命令，那么自动命令会按照定义时的顺序执行。通过 <code>:au</code> 就可以查看它们的执行顺序。</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">au</span> BufNewFile,BufRead *.rb,*.rbw <span class="keyword">setf</span> <span class="keyword">ruby</span></span><br></pre></td></tr></table></figure><p><code>BufNewFile</code> 与 <code>BufRead</code> 事件是被写在 Vim 源文件中的。因此，每当你通过 <code>:e</code> 或者类似的命令打开文件，这两个事件都会触发。然后，就是读取 <code>filetype.vim</code> 文件来判断打开的文件类型。</p><p>简单来说，事件和自动命令在 Vim 中的应用十分广泛。而且，Vim 为我们留出了一些易用的接口，方便用户配置适合自己的事件驱动回调。</p><p>请参阅 <code>:h autocommand</code> 来获取更多帮助</p><h2 id="变更历史，跳转历史"><a href="#变更历史，跳转历史" class="headerlink" title="变更历史，跳转历史"></a>变更历史，跳转历史</h2><p>在 Vim 中，用户最近 100 次的文字改动都会被保存在<strong>变更历史</strong>中。如果在同一行有多个小改动，那么 Vim 会把它们合并成一个。尽管内容改动会合并，但作用的位置还是会只记录下最后一次改动的位置。</p><p>在你移动光标或跳转的时候，每一次的移动或跳转前的位置会被记录到<strong>跳转历史</strong>中。类似地，跳转历史也可以最多保存 100 条记录。对于每个窗口，跳转记录是独立的。但当你分离窗口时（比如使用 <code>:split</code> 命令），跳转历史会被复制过去。</p><p>Vim 中的跳转命令，包括 <code>&#39;</code>、<code>`</code>、<code>G</code>、<code>/</code>、<code>?</code>、<code>n</code>、<code>N</code>、<code>%</code>、<code>(</code>、<code>)</code>、<code>[[</code>、<code>]]</code>、<code>{</code>、<code>}</code>、<code>:s</code>、<code>:tag</code>、<code>L</code>、<code>M</code>、<code>H</code> 以及开始编辑一个新文件的命令。</p><table><thead><tr><th>列表</th><th>显示所有条目</th><th>跳转到上一个位置</th><th>跳转到下一个位置</th></tr></thead><tbody><tr><td>跳转历史</td><td><code>:jumps</code></td><td><code>[count]&lt;c-o&gt;</code></td><td><code>[count]&lt;c-i&gt;</code></td></tr><tr><td>变更历史</td><td><code>:changes</code></td><td><code>[count]g;</code></td><td><code>[count]g,</code></td></tr></tbody></table><p>如果你执行第二列的命令显示所有条目，这时 Vim 会用 <code>&gt;</code> 标记来为你指示当前位置。通常这个标记位于 1 的下方，也就代表最后一次的位置。</p><p>如果你希望关闭 Vim 之后还保留这些条目，请参阅 <code>:h viminfo-&#39;</code> 来获取更多帮助。</p><p><strong>注意</strong>：上面提到过，最后一次跳转前的位置也会记录在<a href="#标注">标注</a>中，也可以通过连按 <kbd>``</kbd> 或 <kbd>‘’</kbd> 跳转到那个位置</p><p>请参阅以下两个命令来获取更多帮助：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">:h changelist</span><br><span class="line">:h jumplist</span><br></pre></td></tr></table></figure><h2 id="内容变更历史记录"><a href="#内容变更历史记录" class="headerlink" title="内容变更历史记录"></a>内容变更历史记录</h2><p>Vim 会记录文本改变之前的状态。因此，你可以使用「撤销」操作 <kbd>u</kbd> 来取消更改，也可以通过「重做」操作 <kbd>Ctrl + r</kbd> 来恢复更改。</p><p>值得注意的是，Vim 采用 <a href="https://en.wikipedia.org/wiki/Tree_(data_structure" target="_blank" rel="noopener">tree</a>) 数据结构来存储内容变更的历史记录，而不是采用 <a href="https://en.wikipedia.org/wiki/Queue_(abstract_data_type" target="_blank" rel="noopener">queue</a>)。你的每次改动都会成为存储为树的节点。而且，除了第一次改动（根节点），之后的每次改动都可以找到一个对应的父节点。每一个节点都会记录改动的内容和时间。其中，「分支」代表从任一节点到根节点的路径。当你进行了撤销操作，然后又输入了新的内容，这时候就相当于创建了分支。这个原理和 git 中的 branch（分支）十分类似。</p><p>考虑以下这一系列按键操作：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">ifoo<span class="symbol">&lt;esc&gt;</span></span><br><span class="line">obar<span class="symbol">&lt;esc&gt;</span></span><br><span class="line">obaz<span class="symbol">&lt;esc&gt;</span></span><br><span class="line"><span class="keyword">u</span></span><br><span class="line">oquux<span class="symbol">&lt;exc&gt;</span></span><br></pre></td></tr></table></figure><p>那么现在，Vim 中会显示三行文本，分别是 “foo”、”bar” 和 “quux”。这时候，存储的树形结构如下：</p><pre><code>     foo(1)       /    bar(2)   /      \baz(3)   quux(4)</code></pre><p>这个树形结构共包含四次改动，括号中的数字就代表时间顺序。</p><p>现在，我们有两种方式遍历这个树结构。一种叫「按分支遍历」，一种叫「按时间遍历」。</p><p>撤销 <kbd>u</kbd> 与重做 <kbd>Ctrl + r</kbd> 操作是按分支遍历。对于上面的例子，现在我们有三行字符。这时候按 <kbd>u</kbd> 会回退到 “bar” 节点，如果再按一次 <kbd>u</kbd> 则会回退到 “foo” 节点。这时，如果我们按下 <kbd>Ctrl + r</kbd> 就会前进至 “bar” 节点，再按一次就回前进至 “quux” 节点。在这种方式下，我们无法访问到兄弟节点（即 “baz” 节点）。</p><p>与之对应的是按时间遍历，对应的按键是 <code>g-</code> 和 <code>g+</code>。对于上面的例子，按下 <code>g-</code> 会首先回退到 “baz” 节点。再次按下 <code>g-</code> 会回退到 “bar” 节点。</p><table><thead><tr><th>命令/按键</th><th>执行效果</th></tr></thead><tbody><tr><td><code>[count]u</code> 或 <code>:undo [count]</code></td><td>回退到 <code>[count]</code> 次改动之前</td></tr><tr><td><code>[count]&lt;c-r&gt;</code> 或 <code>:redo [count]</code></td><td>重做 <code>[count]</code> 次改动</td></tr><tr><td><code>U</code></td><td>回退至最新的改动</td></tr><tr><td><code>[count]g-</code> 或 <code>:earlier [count]?</code></td><td>根据时间回退到 <code>[count]</code> 次改动之前。”?” 为 “s”、”m”、”h”、”d” 或 “f”之一。例如，<code>:earlier 2d</code> 会回退到两天之前。<code>:earlier 1f</code> 则会回退到最近一次文件保存时的内容</td></tr><tr><td><code>[count]g+</code> 或 <code>:later [count]?</code></td><td>类似 <code>g-</code>，但方向相反</td></tr></tbody></table><p>内容变更记录会储存在内存中，当 Vim 退出时就会清空。如果需要持久化存储内容变更记录，请参阅<a href="#备份文件交换文件撤销文件以及viminfo文件的处理">备份文件，交换文件，撤销文件以及viminfo文件的处理</a>章节的内容。</p><p>如果你觉得这一部分的内容难以理解，请参阅 <a href="https://github.com/mbbill/undotree" target="_blank" rel="noopener">undotree</a>，这是一个可视化管理内容变更历史记录的插件。类似的还有 <a href="https://github.com/simnalamburt/vim-mundo" target="_blank" rel="noopener">vim-mundo</a>。</p><p>请参阅以下链接获取更多帮助：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">:h <span class="keyword">undo</span>.txt</span><br><span class="line">:h usr_32</span><br></pre></td></tr></table></figure><h2 id="全局位置信息表，局部位置信息表"><a href="#全局位置信息表，局部位置信息表" class="headerlink" title="全局位置信息表，局部位置信息表"></a>全局位置信息表，局部位置信息表</h2><p>在某一个动作返回一系列「位置」的时候，我们可以利用「全局位置信息表」和「局部位置信息表」来存储这些位置信息，方便以后跳转回对应的位置。每一个存储的位置包括文件名、行号和列号。</p><p>比如，编译代码是出现错误，这时候我们就可以把错误的位置直接显示在全局位置信息表，或者通过外部抓取工具使位置显示在局部位置信息表中。</p><p>尽管我们也可以把这些信息显示到一个空格缓冲区中，但用这两个信息表显示的好处在于接口调用很方便，而且也便于浏览输出。</p><p>Vim 中，全局位置信息表只能有一个，但每一个窗口都可以有自己的局部位置信息表。这两个信息表的外观看上去很类似，但在操作上会稍有不同。</p><p>以下为两者的操作比较：</p><table><thead><tr><th>动作</th><th>全局位置信息表</th><th>局部位置信息表</th></tr></thead><tbody><tr><td>打开窗口</td><td><code>:copen</code></td><td><code>:lopen</code></td></tr><tr><td>关闭窗口</td><td><code>:cclose</code></td><td><code>:lclose</code></td></tr><tr><td>下一个条目</td><td><code>:cnext</code></td><td><code>:lnext</code></td></tr><tr><td>上一个条目</td><td><code>:cprevious</code></td><td><code>:lprevious</code></td></tr><tr><td>第一个条目</td><td><code>:cfirst</code></td><td><code>:lfirst</code></td></tr><tr><td>最后一个条目</td><td><code>:clast</code></td><td><code>:llast</code></td></tr></tbody></table><p>请参阅 <code>:h :cc</code> 以及底下的内容，来获取更多命令的帮助。</p><p><strong>应用实例</strong>：<br>如果我们想用 <code>grep</code> 递归地在当前文件夹中寻找某个关键词，然后把输出结果放到全局位置信息表中，只需要这样：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">:<span class="keyword">let</span> &amp;grepprg = <span class="string">'grep -Rn $* .'</span></span><br><span class="line">:grep! foo</span><br><span class="line">&lt;<span class="keyword">grep</span> output - hit enter&gt;</span><br><span class="line">:<span class="keyword">copen</span></span><br></pre></td></tr></table></figure><p>执行了上面的代码，你就能看到所有包含字符串 “foo” 的文件名以及匹配到的相关字段都会显示在全局位置信息表中。</p><h2 id="宏"><a href="#宏" class="headerlink" title="宏"></a>宏</h2><p>你可以在 Vim 中录制一系列按键，并把他们存储到<a href="#寄存器">寄存器</a>中。对于一些需要临时使用多次的一系列操作，把它们作为宏保存起来会显著地提升效率。对于一些复杂的操作，建议使用 Vim 脚本来实现。</p><ul><li>首先，按下 <kbd>q</kbd>，然后按下你想要保存的寄存器，任何小写字母都可以。比如我们来把它保存到 <code>q</code> 这个寄存器中。按下 <code>qq</code>，你会发现命令行里已经显示了 “recording @q”。</li><li>如果你已经录制完成，那么只需要再按一次 <kbd>q</kbd> 就可以结束录制。</li><li>如果你想调用刚才录制的宏，只需要 <code>[count]@q</code></li><li>如果你想调用上一次使用的宏，只需要 <code>[count]@@</code></li></ul><p><strong>实例1</strong>：</p><p>一个插入字符串 “abc” 后换行的宏，重复调用十次：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">qq</span><br><span class="line"><span class="keyword">iabc</span><span class="symbol">&lt;cr&gt;</span><span class="symbol">&lt;esc&gt;</span></span><br><span class="line">q</span><br><span class="line"><span class="number">10</span>@q</span><br></pre></td></tr></table></figure><p>（对于上面这个功能，你同样可以通过如下的按键： <kbd>o</kbd><kbd>a</kbd><kbd>b</kbd><kbd>c</kbd> 然后 <kbd>ESC</kbd> 然后 <kbd>1</kbd><kbd>0</kbd><kbd>.</kbd> 来实现）。</p><p><strong>实例2</strong>：</p><p>一个在每行前都加上行号的宏。从第一行开始，行号为 1，后面依次递增。我们可以通过 <kbd>Ctrl</kbd> + <kbd>a</kbd> 来实现递增的行号，在定义宏的时候，它会显示成 <code>^A</code>。</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">qq</span><br><span class="line"><span class="number">0</span>yf jP0^A</span><br><span class="line">q</span><br><span class="line"><span class="number">1000</span> @q</span><br></pre></td></tr></table></figure><p>这里能实现功能，是因为我们假定了文件最多只有 1000 行。但更好的方式是使用「递归」宏，它会一直执行，知道不能执行为止：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">qq</span><br><span class="line"><span class="number">0</span>yf jP0^A@q</span><br><span class="line">q</span><br><span class="line">@q</span><br></pre></td></tr></table></figure><p>（对于上面这个插入行号的功能，如果你不愿意使用宏，同样可以通过这段按键操作来实现：<code>:%s/^/\=line(&#39;.&#39;) . &#39;. &#39;</code>）。</p><p>这里向大家展示了如何不用宏来达到相应的效果，但要注意，这些不用宏的实现方式只适用于这些简单的示例。对于一些比较复杂的自动化操作，你确实应该考虑使用宏。</p><p>请参阅以下文档获取更多帮助：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">:h recording</span><br><span class="line">:h <span class="string">'lazyredraw'</span></span><br></pre></td></tr></table></figure><h2 id="颜色主题"><a href="#颜色主题" class="headerlink" title="颜色主题"></a>颜色主题</h2><p>颜色主题可以把你的 Vim 变得更漂亮。Vim 是由多个组件构成的，我们可以给每一个组件都设置不同的文字颜色、背景颜色以及文字加粗等等。比如，我们可以通过这个命令来设置背景颜色：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:<span class="keyword">highlight</span> Normal ctermbg=<span class="number">1</span> guibg=<span class="keyword">red</span></span><br></pre></td></tr></table></figure><p>执行后你会发现，现在背景颜色变成红色了。请参阅 <code>:h :highlight</code> 来获取更多帮助。</p><p>其实，颜色主题就是一系列的 <code>:highlight</code> 命令的集合。</p><p>事实上，大部分颜色主题都包含两套配置。一套适用于例如 xterm 和 iTerm 这样的终端环境（使用前缀 <code>cterm</code>），另一套适用于例如 gvim 和 MacVim 的图形界面环境（使用前缀 <code>gui</code>）。对于上面的例子，<code>ctermbg</code> 就是针对终端环境的，而 <code>guibg</code> 就是针对图形界面环境的。</p><p>如果你下载了一个颜色主题，并且在终端环境中打开了 Vim，然后发现显示的颜色与主题截图中差别很大，那很可能是配置文件只设置了图形界面环境的颜色。反之同理，如果你使用的是图形界面环境，发现显示颜色有问题，那就很可能是配置文件只设置了终端环境的颜色。</p><p>第二种情况（图形界面环境的显示问题）其实不难解决。如果你使用的是 Neovim 或者 Vim 7.4.1830 的后续版本，可以通过打开<a href="https://zh.wikipedia.org/wiki/真彩色" target="_blank" rel="noopener">真彩色</a>设置来解决显示问题。这就可以让终端环境的 Vim 使用 GUI 的颜色定义，但首先，你要确认一下你的终端环境和环境内的组件（比如 tmux）是否都支持真彩色。可以看一下<a href="https://gist.github.com/XVilka/8346728" target="_blank" rel="noopener">这篇文档</a>，描述的十分详细。</p><p>请参阅以下文档或链接来获取更多帮助：</p><ul><li><code>:h &#39;termguicolors&#39;</code></li><li><a href="#主题列表">主题列表</a></li><li><a href="#自定义主题中的颜色">自定义主题中的颜色</a></li></ul><h2 id="折叠"><a href="#折叠" class="headerlink" title="折叠"></a>折叠</h2><p>每一部分文字（或者代码）都会有特定的结构。对于存在结构的文字和代码，也就意味着它们可以按照一定的逻辑分割成不同区域。Vim 中的折叠功能，就是按照特定的逻辑把文字和代码折叠成一行，并显示一些简短的描述。折叠功能涉及到很多操作，而且折叠功能可以嵌套使用。</p><p>在 Vim 中，有以下 6 中折叠类型：</p><table><thead><tr><th>折叠方式</th><th>概述</th></tr></thead><tbody><tr><td>diff</td><td>在「比较窗口」中折叠未改变的文本</td></tr><tr><td>expr</td><td>使用 <code>&#39;foldexpr&#39;</code> 来创建新的折叠逻辑</td></tr><tr><td>indent</td><td>基于缩进折叠</td></tr><tr><td>manual</td><td>使用 <code>zf</code>、<code>zF</code> 或 <code>:fold</code> 来自定义折叠</td></tr><tr><td>marker</td><td>根据特定的文本标记折叠（通常用于代码注释）</td></tr><tr><td>syntax</td><td>根据语法折叠，比如折叠 <code>if</code> 代码块</td></tr></tbody></table><p><strong>注意</strong>：折叠功能可能会显著地影响性能。如果你在使用折叠功能的时候出现了打字卡顿之类的问题，请考虑使用 <a href="https://github.com/Konfekt/FastFold" target="_blank" rel="noopener">FastFold 插件</a>。这个插件可以让 Vim 按需更新折叠内容，而不是一直调用。</p><p>请参阅以下文档获取更多帮助：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">:h usr_28</span><br><span class="line">:h folds</span><br></pre></td></tr></table></figure><h2 id="会话"><a href="#会话" class="headerlink" title="会话"></a>会话</h2><p>如果你保存了当前的「视图」（请参阅 <code>:h :mkview</code>），那么当前窗口、配置和按键映射都会被保存下来（请参阅 <code>:h :loadview</code>）。</p><p>「会话」就是存储所有窗口的相关设置，以及全局设置。简单来说，就是给当前的 Vim 运行实例拍个照，然后把相关信息存储到会话文件中。存储之后的改动就不会在会话文件中显示，你只需要在改动后更新一下会话文件就可以了。</p><p>你可以把当前工作的「项目」存储起来，然后可以在不同的「项目」之间切换。</p><p>现在就来试试吧。打开几个窗口和标签，然后执行 <code>:mksession Foo.vim</code>。如果你没有指定文件名，那就会默认保存为 <code>Session.vim</code>。这个文件会保存在当前的目录下，你可以通过 <code>:pwd</code> 来显示当前路径。重启 Vim 之后，你只需要执行 <code>:source Foo.vim</code>，就可以恢复刚才的会话了。所有的缓冲区、窗口布局、按键映射以及工作路径都会恢复到保存时的状态。</p><p>其实 Vim 的会话文件就只是 Vim 命令的集合。你可以通过命令 <code>:vs Foo.vim</code> 来看看会话文件中究竟有什么。</p><p>你可以决定 Vim 会话中究竟要保存哪些配置，只需要设置一下 <code>&#39;sessionoptions&#39;</code> 就可以了。</p><p>为了方便开发，Vim 把最后一次调用或写入的会话赋值给了一个内部变量 <code>v:this_session</code>。</p><p>请参阅以下文档来获取更多帮助：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">:h Session</span><br><span class="line">:h <span class="string">'sessionoptions'</span></span><br><span class="line">:h <span class="variable">v:this_session</span></span><br></pre></td></tr></table></figure><h2 id="局部化"><a href="#局部化" class="headerlink" title="局部化"></a>局部化</h2><p>以上提到的很多概念，都有一个局部化（非全局）的版本：</p><table><thead><tr><th>全局</th><th>局部</th><th>作用域</th><th>帮助文档</th></tr></thead><tbody><tr><td><code>:set</code></td><td><code>:setlocal</code></td><td>缓冲区或窗口</td><td><code>:h local-options</code></td></tr><tr><td><code>:map</code></td><td><code>:map &lt;buffer&gt;</code></td><td>缓冲区</td><td><code>:h :map-local</code></td></tr><tr><td><code>:autocmd</code></td><td><code>:autocmd * &lt;buffer&gt;</code></td><td>缓冲区</td><td><code>:h autocmd-buflocal</code></td></tr><tr><td><code>:cd</code></td><td><code>:lcd</code></td><td>窗口</td><td><code>:h :lcd</code></td></tr><tr><td><code>:&lt;leader&gt;</code></td><td><code>:&lt;localleader&gt;</code></td><td>缓冲区</td><td><code>:h maploacalleader</code></td></tr></tbody></table><p>变量也有不同的作用域，详细内容请参考 <a href="http://vimdoc.sourceforge.net/htmldoc/usr_41.html" target="_blank" rel="noopener">Vim scripting 的文档</a>。</p><h1 id="用法"><a href="#用法" class="headerlink" title="用法"></a>用法</h1><h2 id="获取离线帮助"><a href="#获取离线帮助" class="headerlink" title="获取离线帮助"></a>获取离线帮助</h2><p>Vim 自带了一套很完善的帮助文档，它们是一个个有固定排版格式的文本文件，通过标签可以访问这些文件的特定位置。</p><p>在开始之前先读一下这个章节：<code>:help :help</code>。执行这个命令以后会在新窗口打开 <code>$VIMRUNTIME/doc/helphelp.txt</code> 文件并跳转到这个文件中 <code>:help</code> 标签的位置。</p><p>一些关于帮助主题的简单规则：</p><ul><li>用单引号把文本包起来表示选项，如：<code>:h &#39;textwidth&#39;</code></li><li>以小括号结尾表示 VimL 函数，如：<code>:h reverse()</code></li><li>以英文冒号开头表示命令，如：<code>:h :echo</code></li></ul><p>使用快捷键 <code>&lt;c-d&gt;</code> （这是 <kbd>ctrl</kbd>+<kbd>d</kbd>）来列出所有包含你当前输入的内容的帮助主题。如：<code>:h tab&lt;c-d&gt;</code> 会列出所有包含 <code>tab</code> 主题，从 <code>softtabstop</code> 到 <code>setting-guitablabel</code> （译者注：根据安装的插件不同列出的选项也会不同）。</p><p>你想查看所有的 VimL 方法吗？很简单，只要输入：<code>:h ()&lt;c-d&gt;</code> 就可以了。你想查看所有与窗口相关的函数吗？输入 <code>:h win*()&lt;c-d&gt;</code>。</p><p>相信你很快就能掌握这些技巧，但是在刚开始的时候，你可能对于该通过什么进行查找一点线索都没有。这时你可以想象一些与要查找的内容相关的关键字，再让 <code>:helpgrep</code> 来帮忙。</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:<span class="keyword">helpgrep</span> backwards</span><br></pre></td></tr></table></figure><p>上面的命令会在所有的帮助文件中搜索“backwards”，然后跳转到第一个匹配的位置。所有的匹配位置都会被添加到全局位置信息表，用 <code>:cp / :cn</code> 可以在匹配位置之间进行切换。或者用 <code>:copen</code> 命令来打开全局位置信息表，将光标定位到你想要的位置，再按 回车就可以跳转到该匹配项。详细说明请参考 <code>:h quickfix</code>。</p><h2 id="获取离线帮助（补充）"><a href="#获取离线帮助（补充）" class="headerlink" title="获取离线帮助（补充）"></a>获取离线帮助（补充）</h2><p>这个列表最初发表在 <a href="https://groups.google.com/forum/#!forum/vim_dev" target="_blank" rel="noopener">vim_dev</a>，由 @chrisbra 编辑的，他是 Vim 开发人员中最活跃的一个。</p><p>经过一些微小的改动后，重新发布到了这里。</p><hr><p>如果你知道你想要找什么，使用帮助系统的搜索会更简单一些，因为搜索出的主题都带有固定的格式。</p><p>而且帮助系统中的主题包含了你当前使用的 Vim 版本的所特有特性，而网上那些已经过时或者是早期发布的话题是不会包含这些的。</p><p>因此学习使用帮助系统以及它所用的语言是很有必要的。这里是一些例子（不一定全，我有可能忘了一些什么）。</p><p>（译者注：下面列表中提及的都是如何指定搜索主题以便快速准确的找到你想要的帮助）</p><ol><li><p>选项要用单引号引起来。用 <code>:h &#39;list&#39;</code> 来查看列表选项帮助。只有你明确的知道你要找这么一个选项的时候才可以这么做，不然的话你可以用 <code>:h options.txt</code> 来打开所有选项的帮助页面，再用正则表达式进行搜索，如：<code>/width</code>。某些选项有它们自己的命名空间，如：<code>:h cpo-a</code>，<code>:h cpo-A</code>， <code>:h cpo-b</code> 等等。</p></li><li><p>普通模式的命令不能用冒号作为前缀。使用 <code>:h gt</code> 来转到“gt”命令的帮助页面。</p></li><li><p>正则表达式以“/”开头，所以 <code>:h /\+</code> 会带你到正则表达式中量词“+”的帮助页面。</p></li><li><p>组合键经常以一个字母开头表示它们可以在哪些模式中使用。如：<code>:h i_CTRL-X</code> 会带你到插入模式下的 CTRL-X 命令的用法帮助页面，这是一个自动完成类的组合键。需要注意的是某些键是有固定写法的，如Control键写成CTRL。还有，查找普通模式下的组合键帮助时，可以省略开头的字母“n”，如：<code>:h CTRL-A</code>。而 <code>:h c_CTRL-A</code>（译者注：原文为 <code>:h c_CRTL-R</code>，感觉改为 A 更符合上下文语境）会解释 CTRL-A 在命令模式下输入命令时的作用；<code>:h v_CTRL-A</code> 说的是在可见模式下把光标所在处的数字加 1；<code>:h g_CTRL-A</code> 则说的是 g 命令（你需要先按 “g” 的命令）。这里的 “g” 代表一个普通的命令，这个命令总是与其它的按键组合使用才生效，与 “z” 开始的命令相似。</p></li><li><p>寄存器是以 “quote” 开头的。如：<code>:h quote:</code> （译者注：原文为<code>:h quote</code>，感觉作者想以”:”来举例）来查看关于”:”寄存器的说明。</p></li><li><p>关于 Vim 脚本（VimL）的帮助都在 <code>:h eval.txt</code> 里。而某些方面的语言可以使用 <code>:h expr-X</code> 获取帮助，其中的 ‘X’ 是一个特定的字符，如：<code>:h expr-!</code> 会跳转到描述 VimL 中’!’（非）的章节。另外一个重要提示，可以使用 <code>:h function-list</code> 来查看所有函数的简要描述，列表中包括函数名和一句话描述。</p></li><li><p>关于映射都可以在 <code>:h map.txt</code> 中找到。通过 <code>:h mapmode-i</code> 来查找 <code>:imap</code> 命令的相关信息；通过 <code>:h map-topic</code> 来查找专门针对映射的帮助（译者注：topic 为一个占位符，正如上面的字符 ‘X’ 一样，在实际使用中需要替换成相应的单词）（如：<code>:h :map-local</code> 查询本地buffer的映射，<code>:h map-bar</code> 查询如何在映射中处理’|’)。</p></li><li><p>命令定义用 “command-“ 开头，如用 <code>:h command-bar</code> 来查看自定义命令中’!’的作用。</p></li><li><p>窗口管理类的命令是以 “CTRL-W” 开头的，所以你可以用 <code>:h CTRL-W_*</code> 来查找相应的帮助（译者注：’*‘同样为占位符）（如：<code>:h CTRL-W_p</code> 查看切换到之前访问的窗口命令的解释）。如果你想找窗口处理的命令，还可以通过访问 <code>:h windows.txt</code> 并逐行向下浏览，所有窗口管理的命令都在这里了。</p></li><li><p>执行类的命令以”:”开头，即：<code>:h :s</code> 讲的是 “:s” 命令。</p></li><li><p>在输入某个话题时按 CTRL-D，让 Vim 列出所有的近似项辅助你输入。</p></li><li><p>用 <code>:helpgrep</code> 在所有的帮助页面（通常还包括了已安装的插件的帮助页面）中进行搜索。参考 <code>:h :helpgrep</code> 来了解如何使用。当你搜索了一个话题之后，所有的匹配结果都被保存到了全局位置信息表（或局部位置信息表）当中，可以通过 <code>:copen</code> 或 <code>:lopen</code> 打开。在打开的窗口中可能通过 <code>/</code> 对搜索结果进行进一步的过滤。</p></li><li><p><code>:h helphelp</code> 里介绍了如何使用帮助系统。</p></li><li><p>用户手册。它采用了一种对初学者更加友好的方式来展示帮助话题。用 <code>:h usr_toc.txt</code> 打开目录（你可能已经猜到这个命令的用处了）。浏览用户手册能帮助你找出某些你想了解的话题，如你可以在第24章看到关于“复合字符”以及“输入特殊字符”的讲解（用 <code>:h usr_24.txt</code> 可以快速打开相关章节）。</p></li><li><p>高亮分组的帮助以 <code>hl-</code> 开头。如：<code>:h hl-WarningMsg</code> 说的是警告信息分组的高亮。</p></li><li><p>语法高亮以<code>:syc-</code> 开头，如：<code>:h :syn-conceal</code> 讲的是 <code>:syn</code> 命令的对于隐藏字符是如何显示的。</p></li><li><p>快速修复命令以 <code>:c</code> 开头，而位置列表命令以 <code>:l</code> 开头。</p></li><li><p><code>:h BufWinLeave</code> 讲的是 BufWinLeave 自动命令。还有，<code>:h autocommand-events</code> （译者注：原文是 <code>:h autocommands-events</code>，但是没有该帮助）讲的是所有可用的事件。</p></li><li><p>启动参数都以“-”开头，如：<code>:h -f</code> 会告诉你 Vim 中 “-f” 参数的作用。</p></li><li><p>额外的特性都以“+”开头，如：<code>:h +conceal</code> 讲的是关于隐藏字符的支持。</p></li><li><p>错误代码可以在帮助系统中直接查到。<code>:h E297</code> 会带你到关于这一错误的详细解释。但是有时并没有转到错误描述，而是列出了经常导出这一错误的 Vim 命令，如 <code>:h E128</code> （译者注：原文为<code>:h hE128</code>，但是并没有该帮助）会直接跳转到 <code>:function</code> 命令。</p></li><li><p>关于包含的语法文件的文档的帮助话题格式是 <code>:h ft-*-syntax</code>。如：<code>:h ft-c-syntax</code> 说的就是C语言语法文件以及它所提供的选项。有的语法文件还会带有自动完成（<code>:h ft-php-omni</code>）或文件类型插件（<code>:h ft-tex-plugin</code>）相关的章节可以查看。</p></li></ol><p>另外在每个帮助页的顶端通常会包含一个用户文档链接（更多的从从用户的角度出发来主角命令的功能和用法，不涉及那么多细节）。如：<code>:h pattern.txt</code> 里包含了 <code>:h 03.9</code> 和 <code>:h usr_27</code> 两个章节的链接。</p><h2 id="获取在线帮助"><a href="#获取在线帮助" class="headerlink" title="获取在线帮助"></a>获取在线帮助</h2><p>如果你遇到了无法解决的问题，或者需要指引的话，可以参考 <a href="https://groups.google.com/forum/#!forum/vim_use" target="_blank" rel="noopener">Vim 使用</a>邮件列表。 <a href="https://de.wikipedia.org/wiki/Internet_Relay_Chat" target="_blank" rel="noopener">IRC</a> 也是一个很不错的资源。 <a href="https://freenode.net/" target="_blank" rel="noopener">Freenode</a> 上的 <code>#vim</code> 频道很庞大，并且里面有许多乐于助人的人。</p><p>如果你想给 Vim 提交 Bug 的话，可以使用 <a href="https://groups.google.com/forum/#!forum/vim_dev" target="_blank" rel="noopener">vim_dev</a> 邮件列表。</p><h2 id="执行自动命令"><a href="#执行自动命令" class="headerlink" title="执行自动命令"></a>执行自动命令</h2><p>你可以触发任何事件，如：<code>:doautocmd BufRead</code>。</p><h3 id="用户自定义事件"><a href="#用户自定义事件" class="headerlink" title="用户自定义事件"></a>用户自定义事件</h3><p>对于插件而言，创建你自己的自定义事件有时非常有用。</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span>! <span class="title">Chibby</span><span class="params">()</span></span></span><br><span class="line">    <span class="comment">" A lot of stuff is happening here.</span></span><br><span class="line">    <span class="comment">" And at last..</span></span><br><span class="line">    <span class="keyword">doautocmd</span> User ChibbyExit</span><br><span class="line"><span class="keyword">endfunction</span></span><br></pre></td></tr></table></figure><p>现在你插件的用户可以在Chibby执行完成之后做任何他想做的事情：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">autocmd</span> User ChibbyExit <span class="keyword">call</span> ChibbyCleanup()</span><br></pre></td></tr></table></figure><p>顺便提一句，如果在使用 <code>:autocmd</code> 或 <code>:doautocmd</code> 时没有捕捉异常，那么会输出 “No matching autocommands” 信息。这也是为什么许多插件用 <code>silent doautocmd ...</code> 的原因。但是这也会有不足，那就是你不能再在 :autocmd 中使用 <code>echo &quot;foo&quot;</code> 了，取而代之的是你要使用 <code>unsilent echo &quot;foo&quot;</code> 来输出。</p><p>这就是为什么要在触发事件之前先判断事件是否存在的原因，</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> <span class="built_in">exists</span>(<span class="string">'#User#ChibbyExit'</span>)</span><br><span class="line">  <span class="keyword">doautocmd</span> User ChibbyExit</span><br><span class="line"><span class="keyword">endif</span></span><br></pre></td></tr></table></figure><p>帮助文档：<code>:h User</code></p><h3 id="事件嵌套"><a href="#事件嵌套" class="headerlink" title="事件嵌套"></a>事件嵌套</h3><p>默认情况下，自动命令不能嵌套！如果某个自动命令执行了一个命令，这个命令再依次触发其它的事件，这是不可能的。</p><p>例如你想在每次启动 Vim 的时候自动打开你的 vimrc 文件：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">autocmd</span> VimEnter * <span class="keyword">edit</span> $MYVIMRC</span><br></pre></td></tr></table></figure><p>当你启动 Vim 的时候，它会帮你打开你的 vimrc 文件，但是你很快会注意到这个文件没有任何的高亮，尽管平时它是正常可以高亮的。</p><p>问题在于你的非嵌套自动命令 <code>:edit</code> 不会触发“BufRead”事件，所以并不会把文件类型设置成“vim”，进而 <code>$VIMRUNTIME/syntax/vim.vim</code> 永远不会被引入。详细信息请参考：<code>:au BufRead *.vim</code>。要想完成上面所说的需求，使用下面这个命令：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">autocmd</span> VimEnter * nested <span class="keyword">edit</span> $MYVIMRC</span><br></pre></td></tr></table></figure><p>帮助文档：<code>:h autocmd-nested</code></p><h2 id="剪切板"><a href="#剪切板" class="headerlink" title="剪切板"></a>剪切板</h2><p>如果你想在没有GUI支持的Unix系统中使用 Vim 的 <code>&#39;clipboard&#39;</code> 选项，则需要 <code>+clipboard</code> 以及可选的 <code>+xterm_clipboard</code> 两个<a href="#what-kind-of-vim-am-i-running">特性</a>支持。</p><p>帮助文档：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">:h <span class="string">'clipboard'</span></span><br><span class="line">:h <span class="keyword">gui</span>-clipboard</span><br><span class="line">:h <span class="keyword">gui</span>-selections</span><br></pre></td></tr></table></figure><p>另外请参考：<a href="#持续粘贴为什么我每次都要设置-paste-模式">持续粘贴（为什么我每次都要设置 ‘paste’ 模式</a></p><h3 id="剪贴板的使用（Windows-OSX）"><a href="#剪贴板的使用（Windows-OSX）" class="headerlink" title="剪贴板的使用（Windows, OSX）"></a>剪贴板的使用（Windows, OSX）</h3><p>Windows 自带了<a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms649012(v=vs.85" target="_blank" rel="noopener">剪贴板</a>.aspx)，OSX 则带了一个<a href="https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/PasteboardGuide106/Introduction/Introduction.html#//apple_ref/doc/uid/TP40008100-SW1" target="_blank" rel="noopener">粘贴板</a></p><p>在这两个系统中都可以用大家习惯用的 <code>ctrl+c / cmd+c</code> 复制选择的文本，然后在另外一个应用中用 <code>ctrl+v / cmd+v</code> 进行粘贴。</p><p>需要注意的是复制的文本已经被发送到了剪贴板，所以你在粘贴复制的内容之前关闭这个应用是没有任何问题的。</p><p>每次复制的时候，都会向剪贴板寄存器 <code>*</code> 中写入数据。 而在 Vim 中分别使用 <code>&quot;*y</code> 和 <code>&quot;*p</code> 来进行复制（yank) 和 粘贴（paste)。</p><p>如果你不想每次操作都要指定 <code>*</code> 寄存器，可以在你的 vimrc 中添加如下配置：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">set</span> clipboard=unnamed</span><br></pre></td></tr></table></figure><p>通常情况下复制/删除/放入操作会往 <code>&quot;</code> 寄存器中写入数据，而加上了上面的配置之后 <code>*</code> 寄存器也会被写入同样数据，因此简单的使用 <code>y</code> 和 <code>p</code> 就可以复制粘贴了。</p><p>我再说一遍：使用上面的选项意味着每一次的复制/粘贴，即使在同一个 Vim 窗口里，都会修改剪贴板的内容。你自己决定上面的选项是否适合。</p><p>如果你觉得输入 <code>y</code> 还是太麻烦的话，可以使用下面的设置把在可视模式下选择的内容发送到剪贴板：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">set</span> clipboard=unnamed,autoselect</span><br><span class="line"><span class="keyword">set</span> guioptions+=<span class="keyword">a</span></span><br></pre></td></tr></table></figure><p>帮助文档：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">:h clipboard-unnamed</span><br><span class="line">:h autoselect</span><br><span class="line">:h <span class="string">'go_a'</span></span><br></pre></td></tr></table></figure><h3 id="剪贴板的使用（Linux-BSD-…）"><a href="#剪贴板的使用（Linux-BSD-…）" class="headerlink" title="剪贴板的使用（Linux, BSD, …）"></a>剪贴板的使用（Linux, BSD, …）</h3><p>如果你的系统使用了 <a href="http://www.x.org/wiki" target="_blank" rel="noopener">X 图形界面</a>，事情会变得有一点不同。X 图形界面实现了 <a href="http://www.x.org/releases/X11R7.7/doc/xproto/x11protocol.html" target="_blank" rel="noopener">X 窗口系统协议</a>, 这个协议在1987年发布的主版本11，因此 X 也通常被称为 X11。</p><p>在 X10 版本中，<a href="http://www.x.org/releases/X11R7.7/doc/xorg-docs/icccm/icccm.html#Peer_to_Peer_Communication_by_Means_of_Cut_Buffers" target="_blank" rel="noopener">剪贴缓冲区</a>被用来实现像 <em>clipboard</em> 一样由 X 来复制文本，并且可以被所有的程序访问。现在这个机制在 X 中还存在，但是已经过时了，很多程序都不再使用这一机制。</p><p>近年来数据在程序之间是通过<a href="http://www.x.org/releases/X11R7.7/doc/xorg-docs/icccm/icccm.html#Peer_to_Peer_Communication_by_Means_of_Selections" target="_blank" rel="noopener">选择</a>进行传递的。一共有三种选择，经常用到的有两种：PRIMARY 和 CLIPBOARD。</p><p>选择的工作工模大致是这样的：</p><pre><code>Program A：&lt;ctrl+c&gt;Program A：声称对 CLIPBOARD 的所有权Program B：&lt;ctrl+v&gt;Program B：发现CLIPBOARD的所有权被Program A持有Program B：从Program A请求数据Program A：响应这个请求并发送数据给Program BProgram B：从Program A接收数据并插入到窗口中</code></pre><table><thead><tr><th>选择</th><th>何时使用</th><th>如何粘贴</th><th>如何在 Vim 中访问</th></tr></thead><tbody><tr><td>PRIMARY</td><td>选择文本</td><td>鼠标中键, shift+insert</td><td><code>*</code> 寄存器</td></tr><tr><td>CLIPBOARD</td><td>选择文本并按 <code>ctrl+c</code></td><td><code>ctrl+v</code></td><td><code>+</code>寄存器</td></tr></tbody></table><p><strong>注意</strong>：X 服务器并不会保存选择（不仅仅是 CLIPBOARD 选择）！因此在关闭了相应的程序后，你用 <code>ctrl+c</code> 复制的内容将丢失。</p><p>使用 <code>&quot;*p</code> 来贴粘 PRIMARY 选择中的内容，或者使用 <code>&quot;+y1G</code> 来将整个文件的内容复制到 CLIPBOARD 选择。</p><p>如果你需要经常访问这两个寄存器，可以考虑使用如下配置：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">set</span> clipboard^=unnamed          <span class="comment">" * 寄存器</span></span><br><span class="line"><span class="comment">" 或者</span></span><br><span class="line"><span class="keyword">set</span> clipboard^=unnamedplus      <span class="comment">" + 寄存器</span></span><br></pre></td></tr></table></figure><p>（<code>^=</code> 用来将设置的值加到默认值之前，详见：<code>:h :set^=</code>）</p><p>这会使得所有复制/删除/放入操作使用 <code>*</code> 或 <code>+</code> 寄存器代替默认的未命令寄存器 <code>&quot;</code>。之后你就可以直接使用 <code>y</code> 或 <code>p</code> 访问你的X选择了。</p><p>帮助文档：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">:h clipboard-unnamed</span><br><span class="line">:h clipboard-unnamedplus</span><br></pre></td></tr></table></figure><h2 id="打开文件时恢复光标位置"><a href="#打开文件时恢复光标位置" class="headerlink" title="打开文件时恢复光标位置"></a>打开文件时恢复光标位置</h2><p>如果没有这个设置，每次打开文件时光标都将定位在第一行。而加入了这个设置以后，你就可以恢复到上次关闭文件时光标所在的位置了。</p><p>将下面的配置添加到你的 vimrc 文件：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">autocmd</span> BufReadPost *</span><br><span class="line">    \ <span class="keyword">if</span> <span class="built_in">line</span>(<span class="string">"'\""</span>) &gt; <span class="number">1</span> &amp;&amp; <span class="built_in">line</span>(<span class="string">"'\""</span>) &lt;= <span class="built_in">line</span>(<span class="string">"$"</span>) |</span><br><span class="line">    \   <span class="keyword">exe</span> <span class="string">"normal! g`\""</span> |</span><br><span class="line">    \ <span class="keyword">endif</span></span><br></pre></td></tr></table></figure><p>这是通过判断之前的光标位置是否存在（文件可能被其它程序修改而导致所记录的位置已经不存在了），如果存在的话就执行 <code>g`&quot;</code> （转到你离开时的光标位置但是不更改跳转列表）。</p><p>这需要使用 viminfo 文件：<code>:h viminfo-</code>。</p><h2 id="临时文件"><a href="#临时文件" class="headerlink" title="临时文件"></a>临时文件</h2><p>根据选项的不同， Vim 最多会创建4种工作文件。</p><h3 id="备份文件"><a href="#备份文件" class="headerlink" title="备份文件"></a>备份文件</h3><p>你可以让 Vim 在将修改写入到文件之前先备份原文件。默认情况下， Vim 会保存一个备份文件但是当修改成功写入后会立即删除它（<code>:set writebackup</code>）。如果你想一直保留这个备份文件的话，可以使用 <code>:set backup</code>。而如果你想禁用备份功能的话，可以使用 <code>:set nobackup nowritebackup</code>。</p><p>咱们来看一下上次我在 vimrc 中改了什么：</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ diff ~/.vim/vimrc ~/.vim/files/backup/vimrc-vimbackup</span><br><span class="line">390d389</span><br><span class="line">&lt; <span class="built_in">command</span>! -bar -nargs=* -complete=<span class="built_in">help</span> H helpgrep &lt;args&gt;</span><br></pre></td></tr></table></figure><p>帮助文档：<code>:h backup</code></p><h3 id="交换文件"><a href="#交换文件" class="headerlink" title="交换文件"></a>交换文件</h3><p>假设你有一个非常棒的科幻小说的构思。在按照故事情节已经写了好几个小时几十万字的时候..忽然停电了！而那时你才想起来你上次保存 <code>~/来自外太空的邪恶入侵者.txt</code> 是在.. 好吧，你从来没有保存过。</p><p>但是并非没有希望了！在编辑某个文件的时候， Vim 会创建一个交换文件，里面保存的是对当前文件所有未保存的修改。自己试一下，打开任意的文件，并使用 <code>:swapname</code> 获得当前的交换文件的保存路径。你也可以将 <code>:set noswapfile</code> 加入到 vimrc 中来禁用交换文件。</p><p>默认情况下，交换文件会自动保存在被编辑文件所在的目录下，文件名以 <code>.file.swp</code> 后缀结尾，每当你修改了超过 200 个字符或是在之前 4 秒内没有任何动作时更新它的内容，在你不再编辑这个文件的时候会被删除。你可以自己修改这些数字，详见：<code>:h &#39;updatecount&#39;</code> 和 <code>:h &#39;updatetime&#39;</code>。</p><p>而在断电时，交换文件并不会被删除。当你再次打开 <code>vim ~/来自外太空的邪恶入侵者.txt</code> 时， Vim 会提示你恢复这个文件。</p><p>帮助文档：<code>:h swap-file</code> 和 <code>:h usr_11</code></p><h3 id="撤销文件"><a href="#撤销文件" class="headerlink" title="撤销文件"></a>撤销文件</h3><p><a href="#%E5%86%85%E5%AE%B9%E5%8F%98%E6%9B%B4%E5%8E%86%E5%8F%B2%E8%AE%B0%E5%BD%95">内容变更历史记录</a>是保存在内存中的，并且会在 Vim 退出时清空。如果你想让它持久化到磁盘中，可以设置 <code>:set undofile</code>。这会把文件 <code>~/foo.c</code> 的撤销文件保存在 <code>~/foo.c.un~</code>。</p><p>帮助文档：<code>:h &#39;undofile&#39;</code> 和 <code>:h undo-persistence</code></p><h3 id="viminfo-文件"><a href="#viminfo-文件" class="headerlink" title="viminfo 文件"></a>viminfo 文件</h3><p>备份文件、交换文件和撤销文件都是与文本状态相关的，而 viminfo 文件是用来保存在 Vim 退出时可能会丢失的其它的信息的。包括历史记录（命令历史、搜索历史、输入历史）、寄存器内容、标注、缓冲区列表、全局变量等等。</p><p>默认情况下，viminfo 被保存在 <code>~/.viminfo</code>。</p><p>帮助文档：<code>:h viminfo</code> 和 <code>:h &#39;viminfo&#39;</code></p><h3 id="临时文件管理设置示例"><a href="#临时文件管理设置示例" class="headerlink" title="临时文件管理设置示例"></a>临时文件管理设置示例</h3><p>如果你跟我一样，也喜欢把这些文件放到一个位置（如：<code>~/.vim/files</code>）的话，可以使用下面的配置：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">" 如果文件夹不存在，则新建文件夹</span></span><br><span class="line"><span class="keyword">if</span> !isdirectory($HOME.<span class="string">'/.vim/files'</span>) &amp;&amp; <span class="built_in">exists</span>(<span class="string">'*mkdir'</span>)</span><br><span class="line">  <span class="keyword">call</span> <span class="built_in">mkdir</span>($HOME.<span class="string">'/.vim/files'</span>)</span><br><span class="line"><span class="keyword">endif</span></span><br><span class="line"></span><br><span class="line"><span class="comment">" 备份文件</span></span><br><span class="line"><span class="keyword">set</span> backup</span><br><span class="line"><span class="keyword">set</span> backupdir   =$HOME/.<span class="keyword">vim</span>/<span class="keyword">files</span>/backup/</span><br><span class="line"><span class="keyword">set</span> backupext   =-vimbackup</span><br><span class="line"><span class="keyword">set</span> backupskip  =</span><br><span class="line"><span class="comment">" 交换文件</span></span><br><span class="line"><span class="keyword">set</span> directory   =$HOME/.<span class="keyword">vim</span>/<span class="keyword">files</span>/swap//</span><br><span class="line"><span class="keyword">set</span> updatecount =<span class="number">100</span></span><br><span class="line"><span class="comment">" 撤销文件</span></span><br><span class="line"><span class="keyword">set</span> <span class="built_in">undofile</span></span><br><span class="line"><span class="keyword">set</span> undodir     =$HOME/.<span class="keyword">vim</span>/<span class="keyword">files</span>/<span class="keyword">undo</span>/</span><br><span class="line"><span class="comment">" viminfo 文件</span></span><br><span class="line"><span class="keyword">set</span> viminfo     =<span class="string">'100,n$HOME/.vim/files/info/viminfo</span></span><br></pre></td></tr></table></figure><p>注意：如果你在一个多用户系统中编辑某个文件时， Vim 提示你交换文件已经存在的话，可能是因为有其他的用户此时正在编辑这个文件。而如果将交换文件放到自己的home目录的话，这个功能就失效了。因此服务器非常不建议将这些文件修改到HOME目录，避免多人同时编辑一个文件，却没有任何警告。</p><h2 id="编辑远程文件"><a href="#编辑远程文件" class="headerlink" title="编辑远程文件"></a>编辑远程文件</h2><p>Vim 自带的 netrw 插件支持对远程文件的编辑。实际上它将远程的文件通过 scp 复制到本地的临时文件中，再用那个文件打开一个缓冲区，然后在保存时把文件再复制回远程位置。</p><p>下面的命令在你本地的 VIM 配置与 SSH 远程服务器上管理员想让你使用的配置有冲突时尤其有用：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:<span class="keyword">e</span> scp://bram@awesome.site.<span class="keyword">com</span>/.vimrc</span><br></pre></td></tr></table></figure><p>如果你已经设置了 <code>~/.ssh/config</code>，SSH 会自动读取这里的配置：</p><pre><code>Host awesome    HostName awesome.site.com    Port 1234    User bram</code></pre><p>如果你的 <code>~/.ssh/config</code> 中有以上的内容，那么下面的命令就可以正常执行了：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:<span class="keyword">e</span> scp://awesome/.vimrc</span><br></pre></td></tr></table></figure><p>可以用同样的方法编辑 <code>~/.netrc</code>, 详见：<code>:h netrc-netrc</code>。</p><p>确保你已经看过了 <code>:h netrw-ssh-hack</code> 和 <code>:h g:netrw_ssh_cmd</code>。</p><p>另外一种编辑远程文件的方法是使用 <a href="https://wiki.archlinux.org/index.php/Sshfs" target="_blank" rel="noopener">sshfs</a>，它会用 <a href="https://en.wikipedia.org/wiki/Filesystem_in_Userspace" target="_blank" rel="noopener">FUSE</a> 来挂载远程的文件系统到你本地的系统当中。</p><h2 id="插件管理"><a href="#插件管理" class="headerlink" title="插件管理"></a>插件管理</h2><p><a href="https://github.com/tpope/vim-pathogen" target="_blank" rel="noopener">Pathogen</a>是第一个比较流行的插件管理工具。实际上它只是修改了 <em>runtimepath</em> （<code>:h &#39;rtp&#39;</code>） 来引入所有放到该目录下的文件。你需要自己克隆插件的代码仓库到那个目录。</p><p>真正的插件管理工具会在 Vim 中提供帮助你安装或更新插件的命令。以下是一些常用的插件管理工具：</p><ul><li><a href="https://github.com/Shougo/dein.vim" target="_blank" rel="noopener">dein</a></li><li><a href="https://github.com/junegunn/vim-plug" target="_blank" rel="noopener">plug</a></li><li><a href="https://github.com/MarcWeber/vim-addon-manager" target="_blank" rel="noopener">vim-addon-manager</a></li><li><a href="https://github.com/VundleVim/Vundle.vim" target="_blank" rel="noopener">vundle</a></li></ul><h2 id="多行编辑"><a href="#多行编辑" class="headerlink" title="多行编辑"></a>多行编辑</h2><p>这是一种可以同时输入多行连续文本的技术。参考这个<a href="https://raw.githubusercontent.com/mhinz/vim-galore/master/contents/images/content-block_insert.gif" target="_blank" rel="noopener">示例</a>。</p><p>用 <code>&lt;c-v&gt;</code> 切换到可视块模式。然后向下选中几行，按 <code>I</code> 或 <code>A</code> （译者注：大写字母，即 shift+i 或 shift+a）然后开始输入你想要输入的文本。</p><p>在刚开始的时候可能会有些迷惑，因为文本只出现在了当前编辑的行，只有在当前的插入动作结束后，之前选中的其它行才会出现插入的文本。</p><p>举一个简单的例子：<code>&lt;c-v&gt;3jItext&lt;esc&gt;</code>。</p><p>如果你要编辑的行长度不同，但是你想在他们后面追加相同的内容的话，可以试一下这个：<code>&lt;c-v&gt;3j$Atext&lt;esc&gt;</code>。</p><p>有时你可能需要把光标放到当前行末尾之后，默认情况下你是不可能做到的，但是可能通过设置 <code>virtualedit</code> 选项达到目的：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">set</span> virtualedit=<span class="keyword">all</span></span><br></pre></td></tr></table></figure><p>设置之后 <code>$10l</code> 或 <code>90|</code> 都会生效，即使超过了行尾的长度。</p><p>详见 <code>:h blockwise-examples</code>。在开始的时候可能会觉得有些复杂，但是它很快就会成为你的第二天性的。</p><p>如果你想探索更有趣的事情，可以看看<a href="https://github.com/terryma/vim-multiple-cursors" target="_blank" rel="noopener">多光标</a></p><h2 id="使用外部程序和过滤器"><a href="#使用外部程序和过滤器" class="headerlink" title="使用外部程序和过滤器"></a>使用外部程序和过滤器</h2><p>免责声明：Vim 是单线程的，因此在 Vim 中以前端进程执行其它的程序时会阻止其它的一切。当然你可以使用 Vim 程序接口，如Lua，并且使用它的多线程支持，但是在那期间， Vim 的处理还是被阻止了。Neovim 添加了任务 API 解决了此问题。</p><p>（据说 Bram 正在考虑在 Vim 中也添加任务控制。如果你使用了较新版本的的 Vim ，可以看一下 <code>:helpgrep startjob</code>。）</p><p>使用 <code>:!</code> 启动一个新任务。如果你想列出当前工作目录下的所有文件，可以使用 <code>:!ls</code>。 用 <code>|</code> 来将结果通过管道重定向，如：<code>:!ls -l | sort | tail -n5</code>。</p><p>没有使用范围时（译者注：范围就是 <code>:</code> 和 <code>!</code> 之间的内容，<code>.</code> 表示当前行，<code>+4</code> 表示向下偏移4行，<code>$</code> 表示最末行等，多行时用 <code>,</code> 将它们分开，如 <code>.,$</code> 表示从当前行到末行），<code>:!</code> 会显示在一个可滚动的窗口中（译者注：在 GVim 和在终端里运行的结果稍有不同）。相反的，如果指定了范围，这些行会被<a href="https://en.wikipedia.org/wiki/Filter_(software" target="_blank" rel="noopener">过滤</a>)。这意味着它们会通过管道被重定向到过滤程序的 <a href="https://en.wikipedia.org/wiki/Standard_streams#Standard_input_.28stdin.29" target="_blank" rel="noopener">stdin</a>，在处理后再通过过滤程序的 <a href="https://en.wikipedia.org/wiki/Standard_streams#Standard_output_.28stdout.29" target="_blank" rel="noopener">stdout</a> 输出，用输出结果替换范围内的文本。例如：为接下来的5行文本添加行号，可以使用：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:.,+<span class="number">4</span>!nl -<span class="keyword">ba</span> -w1 -s<span class="string">' '</span></span><br></pre></td></tr></table></figure><p>由于手动添加范围很麻烦， Vim 提供了一些辅助方法以方便的添加范围。如果需要经常带着范围的话，你可以在可见模式中先选择，然后再按 <code>:</code> （译者注：选中后再按 <code>!</code> 更方便）。还可以使用 <code>!</code> 来取用一个motion的范围，如 <code>!ipsort</code> （译者注：原文为 <code>!ip!sort</code> ，但经过实验发现该命令执行报错，可能是因为 Vim 版本的原因造成的，新版本使用 <code>ip</code> 选择当前段落后自动在命令后添加了 <code>!</code> ，按照作者的写法来看，可能之前的版本没有自动添加 <code>!</code> ）可以将当前段落的所有行按字母表顺序进行排序。</p><p>一个使用过滤器比较好的案例是<a href="https://golang.org/" target="_blank" rel="noopener">Go语言</a>。它的缩进语法非常个性，甚至还专门提供了一个名为 <code>gofmt</code> 的过滤器来对Go语言的源文件进行正确的缩进。Go语言的插件通常会提供一个名为 <code>:Fmt</code> 的函数，这个函数就是执行了 <code>:%!gofmt</code> 来对整个文件进行缩进。</p><p>人们常用 <code>:r !prog</code> 将prog程序的插入放到当前行的下面，这对于脚本来说是很不错的选择，但是在使用的过程中我发现 <code>!!ls</code> 更加方便，它会用输出结果替换当前行的内容。（译者注：前面命令中的 <code>prog</code> 只是个占位符，在实际使用中需要替换成其它的程序，如 <code>:r !ls</code>，这就与后面的 <code>!!ls</code> 相对应了，两者唯一的不同是第一个命令不会覆盖当前行内容，但是第二个命令会）</p><p>帮助文档：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">:h <span class="built_in">filter</span></span><br><span class="line">:h :read!</span><br></pre></td></tr></table></figure><h2 id="Cscope"><a href="#Cscope" class="headerlink" title="Cscope"></a>Cscope</h2><p><a href="http://cscope.sourceforge.net/" target="_blank" rel="noopener">Cscope</a> 的功能比 <a href="http://ctags.sourceforge.net/" target="_blank" rel="noopener">ctags</a> 要完善，但是只支持C（通过设置cscope.files后同样支持C++以及Java）。</p><p>鉴于Tag文件只是知道某个符号是在哪里定义的，cscope的数据库里的数据信息就多的多了：</p><ul><li>符号是在哪里定义的？</li><li>符号是在哪里被使用的？</li><li>这个全局符号定义了什么？</li><li>这个变量是在哪里被赋值的？</li><li>这个函数在源文件的哪个位置？</li><li>哪些函数调用了这个函数？</li><li>这个函数调用了哪些函数？</li><li>“out of space”消息是从哪来的？</li><li>在目录结构中当前的源文件在哪个位置？</li><li>哪些文件引用了这个头文件？</li></ul><h3 id="1-构建数据库"><a href="#1-构建数据库" class="headerlink" title="1. 构建数据库"></a>1. 构建数据库</h3><p>在你项目的根目录执行下面的命令：</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ cscope -bqR</span><br></pre></td></tr></table></figure><p>这条命令会在当前目录下创建三个文件：<code>cscope{,.in,.po}.out</code> 。把它们想象成你的数据库。</p><p>不幸的时 <code>cscope</code> 默认只分析 <code>*.[c|h|y|l]</code> 文件。如果你想在Java项目中使用 cscope ，需要这样做：</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ find . -name <span class="string">"*.java"</span> &gt; cscope.files</span><br><span class="line">$ cscope -bq</span><br></pre></td></tr></table></figure><h3 id="2-添加数据库"><a href="#2-添加数据库" class="headerlink" title="2. 添加数据库"></a>2. 添加数据库</h3><p>打开你新创建的数据库连接：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:<span class="keyword">cs</span> <span class="built_in">add</span> <span class="keyword">cscope</span>.out</span><br></pre></td></tr></table></figure><p>检查连接已经创建成功：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:<span class="keyword">cs</span> show</span><br></pre></td></tr></table></figure><p>（当然你可以添加多个连接。）</p><h3 id="3-查询数据库"><a href="#3-查询数据库" class="headerlink" title="3. 查询数据库"></a>3. 查询数据库</h3><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:<span class="keyword">cs</span> <span class="keyword">find</span> <span class="symbol">&lt;kind&gt;</span> <span class="symbol">&lt;query&gt;</span></span><br></pre></td></tr></table></figure><p>如：<code>:cs find d foo</code> 会列出 <code>foo(...)</code> 调用的所有函数。</p><table><thead><tr><th>Kind</th><th>说明</th></tr></thead><tbody><tr><td>s</td><td><strong>s</strong>ymbol：查找使用该符号的引用</td></tr><tr><td>g</td><td><strong>g</strong>lobal：查找该全局符号的定义</td></tr><tr><td>c</td><td><strong>c</strong>alls：查找调用当前方法的位置</td></tr><tr><td>t</td><td><strong>t</strong>ext：查找出现该文本的位置</td></tr><tr><td>e</td><td><strong>e</strong>grep：使用 egrep 搜索当前单词</td></tr><tr><td>f</td><td><strong>f</strong>ile：打开文件名</td></tr><tr><td>i</td><td><strong>i</strong>ncludes：查询引入了当前文件的文件</td></tr><tr><td>d</td><td><strong>d</strong>epends：查找当前方法调用的方法</td></tr></tbody></table><p>推荐一些比较方便的映射，如：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">nnoremap</span> <span class="symbol">&lt;buffer&gt;</span> <span class="symbol">&lt;leader&gt;</span><span class="keyword">cs</span> :<span class="keyword">cscope</span> <span class="keyword">find</span> s  <span class="symbol">&lt;c-r&gt;</span>=<span class="built_in">expand</span>(<span class="string">'&lt;cword&gt;'</span>)<span class="symbol">&lt;cr&gt;</span><span class="symbol">&lt;cr&gt;</span></span><br><span class="line"><span class="keyword">nnoremap</span> <span class="symbol">&lt;buffer&gt;</span> <span class="symbol">&lt;leader&gt;</span><span class="keyword">cg</span> :<span class="keyword">cscope</span> <span class="keyword">find</span> g  <span class="symbol">&lt;c-r&gt;</span>=<span class="built_in">expand</span>(<span class="string">'&lt;cword&gt;'</span>)<span class="symbol">&lt;cr&gt;</span><span class="symbol">&lt;cr&gt;</span></span><br><span class="line"><span class="keyword">nnoremap</span> <span class="symbol">&lt;buffer&gt;</span> <span class="symbol">&lt;leader&gt;</span><span class="keyword">cc</span> :<span class="keyword">cscope</span> <span class="keyword">find</span> <span class="keyword">c</span>  <span class="symbol">&lt;c-r&gt;</span>=<span class="built_in">expand</span>(<span class="string">'&lt;cword&gt;'</span>)<span class="symbol">&lt;cr&gt;</span><span class="symbol">&lt;cr&gt;</span></span><br><span class="line"><span class="keyword">nnoremap</span> <span class="symbol">&lt;buffer&gt;</span> <span class="symbol">&lt;leader&gt;</span>ct :<span class="keyword">cscope</span> <span class="keyword">find</span> t  <span class="symbol">&lt;c-r&gt;</span>=<span class="built_in">expand</span>(<span class="string">'&lt;cword&gt;'</span>)<span class="symbol">&lt;cr&gt;</span><span class="symbol">&lt;cr&gt;</span></span><br><span class="line"><span class="keyword">nnoremap</span> <span class="symbol">&lt;buffer&gt;</span> <span class="symbol">&lt;leader&gt;</span><span class="keyword">ce</span> :<span class="keyword">cscope</span> <span class="keyword">find</span> <span class="keyword">e</span>  <span class="symbol">&lt;c-r&gt;</span>=<span class="built_in">expand</span>(<span class="string">'&lt;cword&gt;'</span>)<span class="symbol">&lt;cr&gt;</span><span class="symbol">&lt;cr&gt;</span></span><br><span class="line"><span class="keyword">nnoremap</span> <span class="symbol">&lt;buffer&gt;</span> <span class="symbol">&lt;leader&gt;</span><span class="keyword">cf</span> :<span class="keyword">cscope</span> <span class="keyword">find</span> <span class="keyword">f</span>  <span class="symbol">&lt;c-r&gt;</span>=<span class="built_in">expand</span>(<span class="string">'&lt;cfile&gt;'</span>)<span class="symbol">&lt;cr&gt;</span><span class="symbol">&lt;cr&gt;</span></span><br><span class="line"><span class="keyword">nnoremap</span> <span class="symbol">&lt;buffer&gt;</span> <span class="symbol">&lt;leader&gt;</span>ci :<span class="keyword">cscope</span> <span class="keyword">find</span> i ^<span class="symbol">&lt;c-r&gt;</span>=<span class="built_in">expand</span>(<span class="string">'&lt;cfile&gt;'</span>)<span class="symbol">&lt;cr&gt;</span>$<span class="symbol">&lt;cr&gt;</span></span><br><span class="line"><span class="keyword">nnoremap</span> <span class="symbol">&lt;buffer&gt;</span> <span class="symbol">&lt;leader&gt;</span><span class="keyword">cd</span> :<span class="keyword">cscope</span> <span class="keyword">find</span> d  <span class="symbol">&lt;c-r&gt;</span>=<span class="built_in">expand</span>(<span class="string">'&lt;cword&gt;'</span>)<span class="symbol">&lt;cr&gt;</span><span class="symbol">&lt;cr&gt;</span></span><br></pre></td></tr></table></figure><p>所以 <code>:tag</code> （或 <code>&lt;c-]&gt;</code>）跳转到标签定义的文件，而 <code>:cstag</code> 可以达到同样的目的，同时还会打开 cscope 的数据库连接。<code>&#39;cscopetag&#39;</code> 选项使得 <code>:tag</code> 命令自动的像 <code>:cstag</code> 一样工作。这在你已经使用了基于标签的映射时会非常方便。</p><p>帮助文档：<code>:h cscope</code></p><h2 id="MatchIt"><a href="#MatchIt" class="headerlink" title="MatchIt"></a>MatchIt</h2><p>由于 Vim 是用 C 语言编写的，因此许多功能都假设使用类似 C 语言的语法。默认情况下，如果你的光标在 <code>{</code> 或 <code>#endif</code> , 就可以使用 <code>%</code> 跳转到与之匹配的 <code>}</code> 或 <code>#ifdef</code>。</p><p>Vim 自带了一个名为 matchit.vim 的插件，但是默认没有启用。启用后可以用 <code>%</code> 在HTML相匹配的标签或 VimL 的 if/else/endif 块之间进行跳转，它还带来了一些新的命令。</p><h3 id="在-Vim-8-中安装"><a href="#在-Vim-8-中安装" class="headerlink" title="在 Vim 8 中安装"></a>在 Vim 8 中安装</h3><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">" vimrc</span></span><br><span class="line">packadd! matchit</span><br></pre></td></tr></table></figure><h3 id="在-Vim-7-或者更早的版本中安装"><a href="#在-Vim-7-或者更早的版本中安装" class="headerlink" title="在 Vim 7 或者更早的版本中安装"></a>在 Vim 7 或者更早的版本中安装</h3><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">"vimrc</span></span><br><span class="line"><span class="keyword">runtime</span> macros/matchit.<span class="keyword">vim</span></span><br></pre></td></tr></table></figure><p>由于matchit的文档很全面，我建议安装以后执行一次下面的命令：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">:!mkdir -<span class="keyword">p</span> ~/.<span class="keyword">vim</span>/doc</span><br><span class="line">:!cp $VIMRUNTIME/macros/matchit.<span class="keyword">vim</span> ~/.<span class="keyword">vim</span>/doc</span><br><span class="line">:<span class="keyword">helptags</span> ~/.<span class="keyword">vim</span>/doc</span><br></pre></td></tr></table></figure><h3 id="简短的介绍"><a href="#简短的介绍" class="headerlink" title="简短的介绍"></a>简短的介绍</h3><p>至此这个插件已经可以使用了。 参考 <code>:h matchit-intro</code> 来获得支持的命令以及 <code>:h matchit-languages</code> 来获得支持的语言。</p><p>你可以很方便的定义自己的匹配对，如：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">autocmd</span> FileType <span class="keyword">python</span> <span class="keyword">let</span> <span class="variable">b:match_words</span> = <span class="string">'\&lt;if\&gt;:\&lt;elif\&gt;:\&lt;else\&gt;'</span></span><br></pre></td></tr></table></figure><p>之后你就可以在任何的 Python 文件中使用 <code>%</code> （向前）或 <code>g%</code> （向后）在这三个片断之间跳转了。</p><p>帮助文档：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">:h matchit-install</span><br><span class="line">:h matchit</span><br><span class="line">:h <span class="variable">b:match_words</span></span><br></pre></td></tr></table></figure><h1 id="技巧"><a href="#技巧" class="headerlink" title="技巧"></a>技巧</h1><h2 id="聪明地使用-n-和-N"><a href="#聪明地使用-n-和-N" class="headerlink" title="聪明地使用 n 和 N"></a>聪明地使用 n 和 N</h2><p><kbd>n</kbd> 与 <kbd>N</kbd> 的实际跳转方向取决于使用 <code>/</code> 还是 <code>?</code> 来执行搜索，其中 <code>/</code> 是向后搜索，<code>?</code> 是向前搜索。一开始我（原作者）觉得这里很难理解。</p><p>如果你希望 <kbd>n</kbd> 始终为向后搜索，<kbd>N</kbd> 始终为向前搜索，那么只需要这样设置：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">nnoremap</span> <span class="symbol">&lt;expr&gt;</span> n  <span class="string">'Nn'</span>[<span class="variable">v:searchforward</span>]</span><br><span class="line"><span class="keyword">nnoremap</span> <span class="symbol">&lt;expr&gt;</span> <span class="keyword">N</span>  <span class="string">'nN'</span>[<span class="variable">v:searchforward</span>]</span><br></pre></td></tr></table></figure><h2 id="聪明地使用命令行历史"><a href="#聪明地使用命令行历史" class="headerlink" title="聪明地使用命令行历史"></a>聪明地使用命令行历史</h2><p>我（原作者）习惯用 <kbd>Ctrl</kbd> + <kbd>p</kbd> 和 <kbd>Ctrl</kbd> + <kbd>n</kbd> 来跳转到上一个/下一个条目。其实这个操作也可以用在命令行中，快速调出之前执行过的命令。</p><p>不仅如此，你会发现 <kbd>上</kbd> 和 <kbd>下</kbd> 其实更智能。如果命令行中已经存在了一些文字，我们可以通过按方向键来匹配已经存在的内容。比如，命令行中现在是 <code>:echo</code>，这时候我们按 <kbd>上</kbd>，就会帮我们补全成 <code>:echo &quot;Vim rocks!&quot;</code>（前提是，之前输入过这段命令）。</p><p>当然，Vim 用户都不愿意去按方向键，事实上我们也不需要去按，只需要设置这样的映射：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">cnoremap</span> <span class="symbol">&lt;c-n&gt;</span> <span class="symbol">&lt;down&gt;</span></span><br><span class="line"><span class="keyword">cnoremap</span> <span class="symbol">&lt;c-p&gt;</span> <span class="symbol">&lt;up&gt;</span></span><br></pre></td></tr></table></figure><p>这个功能，我（原作者）每天都要用很多次。</p><h2 id="智能-Ctrl-l"><a href="#智能-Ctrl-l" class="headerlink" title="智能 Ctrl-l"></a>智能 Ctrl-l</h2><p><kbd>Ctrl</kbd> + <kbd>l</kbd> 的默认功能是清空并「重新绘制」当前的屏幕，就和 <code>:redraw!</code> 的功能一样。下面的这个映射就是执行重新绘制，并且取消通过 <code>/</code> 和 <code>?</code> 匹配字符的高亮，而且还可以修复代码高亮问题（有时候，由于多个代码高亮的脚本重叠，或者规则过于复杂，Vim 的代码高亮显示会出现问题）。不仅如此，还可以刷新「比较模式」（请参阅 <code>:help diff-mode</code>）的代码高亮：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">nnoremap</span> <span class="symbol">&lt;leader&gt;</span><span class="keyword">l</span> :<span class="keyword">nohlsearch</span><span class="symbol">&lt;cr&gt;</span>:<span class="keyword">diffupdate</span><span class="symbol">&lt;cr&gt;</span>:<span class="keyword">syntax</span> <span class="keyword">sync</span> fromstart<span class="symbol">&lt;cr&gt;</span><span class="symbol">&lt;c-l&gt;</span></span><br></pre></td></tr></table></figure><h2 id="禁用错误报警声音和图标"><a href="#禁用错误报警声音和图标" class="headerlink" title="禁用错误报警声音和图标"></a>禁用错误报警声音和图标</h2><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">set</span> noerrorbells</span><br><span class="line"><span class="keyword">set</span> novisualbell</span><br><span class="line"><span class="keyword">set</span> t_vb=</span><br></pre></td></tr></table></figure><p>请参阅 <a href="http://vim.wikia.com/wiki/Disable_beeping" target="_blank" rel="noopener">Vim Wiki: Disable beeping</a>。</p><h2 id="快速移动当前行"><a href="#快速移动当前行" class="headerlink" title="快速移动当前行"></a>快速移动当前行</h2><p>有时，我（原作者）想要快速把当前行上移或下移一行，只需要这样设置映射：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">nnoremap</span> [<span class="keyword">e</span>  :<span class="symbol">&lt;c-u&gt;</span><span class="keyword">execute</span> <span class="string">'move -1-'</span>. <span class="variable">v:count1</span><span class="symbol">&lt;cr&gt;</span></span><br><span class="line"><span class="keyword">nnoremap</span> ]<span class="keyword">e</span>  :<span class="symbol">&lt;c-u&gt;</span><span class="keyword">execute</span> <span class="string">'move +'</span>. <span class="variable">v:count1</span><span class="symbol">&lt;cr&gt;</span></span><br></pre></td></tr></table></figure><p>这个映射，同样可以搭配数字使用，比如连续按下 <kbd>2</kbd> <kbd>]</kbd> <kbd>e</kbd> 就可以把当前行向下移动两行。</p><h2 id="快速添加空行"><a href="#快速添加空行" class="headerlink" title="快速添加空行"></a>快速添加空行</h2><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">nnoremap</span> [<span class="symbol">&lt;space&gt;</span>  :<span class="symbol">&lt;c-u&gt;</span>put! =<span class="built_in">repeat</span>(<span class="built_in">nr2char</span>(<span class="number">10</span>), <span class="variable">v:count1</span>)<span class="symbol">&lt;cr&gt;</span><span class="string">'[</span></span><br><span class="line"><span class="string">nnoremap ]&lt;space&gt;  :&lt;c-u&gt;put =repeat(nr2char(10), v:count1)&lt;cr&gt;</span></span><br></pre></td></tr></table></figure><p>设置之后，连续按下 <kbd>5</kbd> <kbd>[</kbd> <kbd>空格</kbd> 在当前行上方插入 5 个空行。</p><h2 id="快速编辑自定义宏"><a href="#快速编辑自定义宏" class="headerlink" title="快速编辑自定义宏"></a>快速编辑自定义宏</h2><p>这个功能真的很实用！下面的映射，就是在一个新的命令行窗口中读取某一个寄存器（默认为 <code>*</code>）。当你设置完成后，只需要按下 <kbd>回车</kbd> 即可让它生效。</p><p>在录制宏的时候，我经常用这个来更改拼写错误。</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">nnoremap</span> <span class="symbol">&lt;leader&gt;</span><span class="keyword">m</span>  :<span class="symbol">&lt;c-u&gt;</span><span class="symbol">&lt;c-r&gt;</span><span class="symbol">&lt;c-r&gt;</span>=<span class="string">'let @'</span>. <span class="variable">v:register</span> .<span class="string">' = '</span>. <span class="built_in">string</span>(<span class="built_in">getreg</span>(<span class="variable">v:register</span>))<span class="symbol">&lt;cr&gt;</span><span class="symbol">&lt;c-f&gt;</span><span class="symbol">&lt;left&gt;</span></span><br></pre></td></tr></table></figure><p>只需要连续按下 <kbd>leader</kbd> <kbd>m</kbd> 或者 <kbd>“</kbd> <kbd>leader</kbd> <kbd>m</kbd> 就可以调用了。</p><p>请注意，这里之所以要写成 <code>&lt;c-r&gt;&lt;c-r&gt;</code> 是为了确保 <code>&lt;c-r&gt;</code> 执行了。请参阅 <code>:h c_^R^R</code></p><h2 id="快速跳转到源-头-文件"><a href="#快速跳转到源-头-文件" class="headerlink" title="快速跳转到源(头)文件"></a>快速跳转到源(头)文件</h2><p>这个技巧可以用在多种文件类型中。当你从源文件或者头文件中切换到其他文件的时候，这个技巧可以设置「文件标记」（请参阅 <code>:h marks</code>），然后你就可以通过连续按下 <kbd>‘</kbd> <kbd>C</kbd> 或者 <kbd>‘</kbd> <kbd>H</kbd> 快速跳转回去（请参阅 <code>:h &#39;A</code>）。</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">autocmd</span> BufLeave *.&#123;<span class="keyword">c</span>,cpp&#125; <span class="keyword">mark</span> C</span><br><span class="line"><span class="keyword">autocmd</span> BufLeave *.h       <span class="keyword">mark</span> H</span><br></pre></td></tr></table></figure><p><strong>注意</strong>：由于这个标记是设置在 viminfo 文件中，因此请先确认 <code>:set viminfo?</code> 中包含了 <code>:h viminfo-&#39;</code>。</p><h2 id="在-GUI-中快速改变字体大小"><a href="#在-GUI-中快速改变字体大小" class="headerlink" title="在 GUI 中快速改变字体大小"></a>在 GUI 中快速改变字体大小</h2><p>印象中，我（原作者）记得一下代码是来自 tpope’s 的配置文件：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">command! Bigger  :<span class="keyword">let</span> &amp;guifont = <span class="keyword">substitute</span>(&amp;guifont, <span class="string">'\d\+$'</span>, <span class="string">'\=submatch(0)+1'</span>, <span class="string">''</span>)</span><br><span class="line">command! Smaller :<span class="keyword">let</span> &amp;guifont = <span class="keyword">substitute</span>(&amp;guifont, <span class="string">'\d\+$'</span>, <span class="string">'\=submatch(0)-1'</span>, <span class="string">''</span>)</span><br></pre></td></tr></table></figure><h2 id="根据模式改变光标类型"><a href="#根据模式改变光标类型" class="headerlink" title="根据模式改变光标类型"></a>根据模式改变光标类型</h2><p>我（原作者）习惯在普通模式下用块状光标，在插入模式下用条状光标（形状类似英文 “I” 的样子），然后在替换模式中使用下划线形状的光标。</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> <span class="built_in">empty</span>($TMUX)</span><br><span class="line">  <span class="keyword">let</span> &amp;t_SI = <span class="string">"\&lt;Esc&gt;]50;CursorShape=1\x7"</span></span><br><span class="line">  <span class="keyword">let</span> &amp;t_EI = <span class="string">"\&lt;Esc&gt;]50;CursorShape=0\x7"</span></span><br><span class="line">  <span class="keyword">let</span> &amp;t_SR = <span class="string">"\&lt;Esc&gt;]50;CursorShape=2\x7"</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">  <span class="keyword">let</span> &amp;t_SI = <span class="string">"\&lt;Esc&gt;Ptmux;\&lt;Esc&gt;\&lt;Esc&gt;]50;CursorShape=1\x7\&lt;Esc&gt;\\"</span></span><br><span class="line">  <span class="keyword">let</span> &amp;t_EI = <span class="string">"\&lt;Esc&gt;Ptmux;\&lt;Esc&gt;\&lt;Esc&gt;]50;CursorShape=0\x7\&lt;Esc&gt;\\"</span></span><br><span class="line">  <span class="keyword">let</span> &amp;t_SR = <span class="string">"\&lt;Esc&gt;Ptmux;\&lt;Esc&gt;\&lt;Esc&gt;]50;CursorShape=2\x7\&lt;Esc&gt;\\"</span></span><br><span class="line"><span class="keyword">endif</span></span><br></pre></td></tr></table></figure><p>原理很简单，就是让 Vim 在进入和离开插入模式的时候，输出一些序列，请参考 <a href="https://en.wikipedia.org/wiki/Escape_sequence" target="_blank" rel="noopener">escape sequence</a>。Vim 与终端之间的中间层，比如 <a href="https://tmux.github.io" target="_blank" rel="noopener">tmux</a> 会处理并执行上面的代码。</p><p>但上面这个还是有一个缺点的。终端环境的内部原理不尽相同，对于序列的处理方式也稍有不同。因此，上面的代码可能无法在你的环境中运行。甚至，你的运行环境也有可能不支持其他光标形状，请参阅你的 Vim 运行环境的文档。</p><p>好消息是，上面这个代码，可以在 iTerm2 中完美运行。</p><h2 id="防止水平滑动的时候失去选择"><a href="#防止水平滑动的时候失去选择" class="headerlink" title="防止水平滑动的时候失去选择"></a>防止水平滑动的时候失去选择</h2><p>如果你选中了一行或多行，那么你可以用 <kbd>&lt;</kbd> 或 <kbd>&gt;</kbd> 来调整他们的缩进。但在调整之后就不会保持选中状态了。</p><p>你可以连续按下 <kbd>g</kbd> <kbd>v</kbd> 来重新选中他们，请参考 <code>:h gv</code>。因此，你可以这样来配置映射：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">xnoremap</span> &lt;  &lt;<span class="keyword">gv</span></span><br><span class="line"><span class="keyword">xnoremap</span> &gt;  &gt;<span class="keyword">gv</span></span><br></pre></td></tr></table></figure><p>设置好之后，在可视模式中使用 <code>&gt;&gt;&gt;&gt;&gt;</code> 就不会再出现上面提到的问题了。</p><h2 id="重新载入保存文件"><a href="#重新载入保存文件" class="headerlink" title="重新载入保存文件"></a>重新载入保存文件</h2><p>通过<a href="#自动命令">自动命令</a>，你可以在保存文件的同时触发一些其他功能。比如，如果这个文件是一个配置文件，那么就重新载入；或者你还可以对这个文件进行代码风格检查。</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">autocmd</span> BufWritePost $MYVIMRC <span class="keyword">source</span> $MYVIMRC</span><br><span class="line"><span class="keyword">autocmd</span> BufWritePost ~/.Xdefaults <span class="keyword">call</span> <span class="built_in">system</span>(<span class="string">'xrdb ~/.Xdefaults'</span>)</span><br></pre></td></tr></table></figure><h2 id="更加智能的当前行高亮"><a href="#更加智能的当前行高亮" class="headerlink" title="更加智能的当前行高亮"></a>更加智能的当前行高亮</h2><p>我（原作者）很喜欢「当前行高亮」（请参阅 <code>:h cursorline</code>）这个功能，但我只想让这个效果出现在当前窗口，而且在插入模式中关闭这个效果：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">autocmd</span> InsertLeave,WinEnter * <span class="keyword">set</span> cursorline</span><br><span class="line"><span class="keyword">autocmd</span> InsertEnter,WinLeave * <span class="keyword">set</span> nocursorline</span><br></pre></td></tr></table></figure><h2 id="更快的关键字补全"><a href="#更快的关键字补全" class="headerlink" title="更快的关键字补全"></a>更快的关键字补全</h2><p>关键字补全（<code>&lt;c-n&gt;</code> 或 <code>&lt;c-p&gt;</code>）功能的工作方式是，无论 <code>&#39;complete&#39;</code> 设置中有什么，它都会尝试着去补全。这样，一些我们用不到的标签也会出现在补全列表中。而且，它会扫描很多文件，有时候运行起来非常慢。如果你不需要这些，那么完全可以像这样把它们禁用掉：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">set</span> <span class="built_in">complete</span>-=i   <span class="comment">" disable scanning included files</span></span><br><span class="line"><span class="keyword">set</span> <span class="built_in">complete</span>-=t   <span class="comment">" disable searching tags</span></span><br></pre></td></tr></table></figure><h2 id="改变颜色主题的默认外观"><a href="#改变颜色主题的默认外观" class="headerlink" title="改变颜色主题的默认外观"></a>改变颜色主题的默认外观</h2><p>如果你想让状态栏在颜色主题更改后依然保持灰色，那么只需要这样设置：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">autocmd</span> ColorScheme * <span class="keyword">highlight</span> StatusLine ctermbg=darkgray cterm=NONE guibg=darkgray <span class="keyword">gui</span>=NONE</span><br></pre></td></tr></table></figure><p>同理，如果你想让某一个颜色主题（比如 “lucius”）的状态栏为灰色（请使用 <code>:echo color_name</code> 来查看当前可用的所有颜色主题）：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">autocmd</span> ColorScheme lucius <span class="keyword">highlight</span> StatusLine ctermbg=darkgray cterm=NONE guibg=darkgray <span class="keyword">gui</span>=NONE</span><br></pre></td></tr></table></figure><h2 id="命令"><a href="#命令" class="headerlink" title="命令"></a>命令</h2><p>下面的命令都比较有用，最好了解一下。用 <code>:h :&lt;command name&gt;</code> 来了解更多关于它们的信息，如：<code>:h :global</code>。</p><h3 id="global-和-vglobal-在所有匹配行执行命令"><a href="#global-和-vglobal-在所有匹配行执行命令" class="headerlink" title=":global 和 :vglobal - 在所有匹配行执行命令"></a>:global 和 :vglobal - 在所有匹配行执行命令</h3><p>在所有符合条件的行上执行某个命令。如： <code>:global /regexp/ print</code> 会在所有包含 “regexp” 的行上执行 <code>print</code> 命令（译者注：regexp 有正则表达式的意思，该命令同样支持正则表达式，在所有符合正则表达式的行上执行指定的命令）。</p><p>趣闻：你们可能都知道老牌的 grep 命令，一个由 Ken Thompson 编写的过滤程序。它是干什么用的呢？它会输出所有匹配指定正则表达式的行！现在猜一下 <code>:global /regexp/ print</code> 的简写形式是什么？没错！就是 <code>:g/re/p</code> 。 Ken Thompsom 在编写 grep 程序的时候是受了 vi <code>:global</code> 的启发。（译者注： <a href="https://robots.thoughtbot.com/how-grep-got-its-name）" target="_blank" rel="noopener">https://robots.thoughtbot.com/how-grep-got-its-name）</a></p><p>既然它的名字是 <code>:global</code>，理应仅作用在所有行上，但是它也是可以带范围限制的。假设你想使用 <code>:delete</code> 命令删除从当前行到下一个空行（由正则表达式 <code>^$</code> 匹配）范围内所有包含 “foo” 的行：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:,/^$/g/foo/d</span><br></pre></td></tr></table></figure><p>如果要在所有 _不_ 匹配的行上执行命令的话，可以使用 <code>:global!</code> 或是它的别名 <code>:vglobal</code> （ V 代表的是 inVerse ）。</p><h3 id="normal-和-execute-脚本梦之队"><a href="#normal-和-execute-脚本梦之队" class="headerlink" title=":normal 和 :execute - 脚本梦之队"></a>:normal 和 :execute - 脚本梦之队</h3><p>这两个命令经常在 Vim 的脚本里使用。</p><p>借助于 <code>:normal</code> 可以在命令行里进行普通模式的映射。如：<code>:normal! 4j</code> 会令光标下移 4 行（由于加了”!”，所以不会使用自定义的映射 “j”）。</p><p>需要注意的是 <code>:normal</code> 同样可以使用范围数（译者注：参考 <code>:h range</code> 和 <code>:h :normal-range</code> 了解更多），故 <code>:%norm! Iabc</code> 会在所有行前加上 “abc”。</p><p>借助于 <code>:execute</code> 可以将命令和表达式混合在一起使用。假设你正在编辑一个 C 语言的文件，想切换到它的头文件：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:<span class="keyword">execute</span> <span class="string">'edit'</span> <span class="built_in">fnamemodify</span>(<span class="built_in">expand</span>(<span class="string">'%'</span>), <span class="string">':r'</span>) . <span class="string">'.h'</span></span><br></pre></td></tr></table></figure><p>（译者注：头文件为与与源文件同名但是扩展名为 <code>.h</code> 的文件。上面的命令中 expand 获得当前文件的名称，fnamemodify 获取不带扩展名的文件名，再连上 ‘.h’ 就是头文件的文件名了，最后在使用 edit 命令打开这个头文件。）</p><p>这两个命令经常一起使用。假设你想让光标下移 n 行：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">:<span class="keyword">let</span> n = <span class="number">4</span></span><br><span class="line">:<span class="keyword">execute</span> <span class="string">'normal!'</span> n . <span class="string">'j'</span></span><br></pre></td></tr></table></figure><h3 id="重定向消息"><a href="#重定向消息" class="headerlink" title="重定向消息"></a>重定向消息</h3><p>许多命令都会输出消息，<code>:redir</code> 用来重定向这些消息。它可以将消息输出到文件、<a href="#寄存器">寄存器</a>或是某个变量中。</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">" 将消息重定向到变量 `neatvar` 中</span></span><br><span class="line">:<span class="keyword">redir</span> =&gt; neatvar</span><br><span class="line"><span class="comment">" 打印所有寄存器的内容</span></span><br><span class="line">:<span class="keyword">reg</span></span><br><span class="line"><span class="comment">" 结束重定向</span></span><br><span class="line">:<span class="keyword">redir</span> END</span><br><span class="line"><span class="comment">" 输出变量</span></span><br><span class="line">:<span class="keyword">echo</span> neatvar</span><br><span class="line"><span class="comment">" 恶搞一下，我们把它输出到当前缓冲区</span></span><br><span class="line">:<span class="keyword">put</span> =neatvar</span><br></pre></td></tr></table></figure><p>再 Vim 8 中，可以更简单的方式即位：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:put =execute(&apos;reg&apos;)</span><br></pre></td></tr></table></figure><p>（译者注：原文最后一条命令是 <code>:put =nicevar</code> 但是实际会报变量未定义的错误）<br>（实测 neovim/vim8 下没问题）</p><p>帮助文档：<code>:h :redir</code></p><h1 id="调试"><a href="#调试" class="headerlink" title="调试"></a>调试</h1><h2 id="常规建议"><a href="#常规建议" class="headerlink" title="常规建议"></a>常规建议</h2><p>如果你遇到了奇怪的行为，尝试用这个命令重现它：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vim -u NONE -N</span><br></pre></td></tr></table></figure><p>这样会在不引用vimrc（默认设置）的情况下重启vim，并且在 <strong>nocompatible</strong> 模式下（使用vim默认设置而不是vi的）。（搜索 <code>:h --noplugin</code> 命令了解更多启动加载方式）</p><p>如果仍旧能够出现该错误，那么这极有可能是vim本身的bug，请给 <a href="&quot;https://groups.google.com/forum/#!forum/vim_dev&quot;">vim_dev</a> 发送邮件反馈错误，多数情况下问题不会立刻解决，你还需要进一步研究</p><p>许多插件经常会提供新的（默认的/自动的）操作。如果在保存的时候发生了，那么请用 <code>:verb au BufWritePost</code> 命令检查潜在的问题</p><p>如果你在使用一个插件管理工具，将插件行注释调，再进行调试。</p><p>问题还没有解决？如果不是插件的问题，那么肯定是你的自定义的设置的问题，可能是你的 options 或 autocmd 等等。</p><p>到了一行行代码检查的时候了，不断地排除缩小检查范围知道你找出错误，根据二分法的原理你不会花费太多时间的。</p><p>在实践过程中，可能就是这样，把 <code>:finish</code> 放在你的 <strong>vimrc</strong> 文件中间，Vim会跳过它之后的设置。如果问题还在，那么问题就出在<code>:finish</code>之前的设置中，再把<code>:finish</code>放到前一部分设置的中间位置。否则问题就出现在它后面的半部分设置，那么就把<code>:finish</code>放到后半部分的中间位置。不断的重复即可找到。</p><h2 id="调整日志等级"><a href="#调整日志等级" class="headerlink" title="调整日志等级"></a>调整日志等级</h2><p>Vim现在正在使用的另一个比较有用的方法是增加debug信息输出详细等级。现在Vim支持9个等级，可以用<code>:h &#39;verbose&#39;</code>命令查看。</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">:<span class="keyword">e</span> /tmp/foo</span><br><span class="line">:<span class="keyword">set</span> <span class="keyword">verbose</span>=<span class="number">2</span></span><br><span class="line">:<span class="keyword">w</span></span><br><span class="line">:<span class="keyword">set</span> <span class="keyword">verbose</span>=<span class="number">0</span></span><br></pre></td></tr></table></figure><p>这可以显示出所有引用的文件、没有变化的文件或者各种各样的作用于保存的插件。</p><p>如果你只是想用简单的命令来提高等级，也是用 <code>:verbose</code> ，放在其他命令之前，通过计数来指明等级，默认是1.</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">:<span class="keyword">verb</span> <span class="keyword">set</span> <span class="keyword">verbose</span></span><br><span class="line"><span class="comment">"  verbose=1</span></span><br><span class="line">:<span class="number">10</span><span class="keyword">verb</span> <span class="keyword">set</span> <span class="keyword">verbose</span></span><br><span class="line"><span class="comment">"  verbose=10</span></span><br></pre></td></tr></table></figure><p>通常用等级1来显示上次从哪里设置的选项</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">:<span class="keyword">verb</span> <span class="keyword">set</span> ai?</span><br><span class="line"><span class="comment">"      Last set from ~/.vim/vimrc</span></span><br></pre></td></tr></table></figure><p>一般等级越高输出信息月详细。但是不要害怕，亦可以把输出导入到文件中：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:<span class="keyword">set</span> verbosefile=/tmp/foo | <span class="number">15</span><span class="keyword">verbose</span> <span class="keyword">echo</span> <span class="string">"foo"</span> | <span class="keyword">vsplit</span> /tmp/foo</span><br></pre></td></tr></table></figure><p>你可以一开始的时候就打开verbosity，用 <code>-V</code> 选项，它默认设置调试等级为10。 例如：<code>vim -V5</code></p><h2 id="查看启动日志"><a href="#查看启动日志" class="headerlink" title="查看启动日志"></a>查看启动日志</h2><h2 id="查看运行时日志"><a href="#查看运行时日志" class="headerlink" title="查看运行时日志"></a>查看运行时日志</h2><h2 id="vim-脚本调试"><a href="#vim-脚本调试" class="headerlink" title="vim 脚本调试"></a>vim 脚本调试</h2><h2 id="语法文件调试"><a href="#语法文件调试" class="headerlink" title="语法文件调试"></a>语法文件调试</h2><h1 id="杂项"><a href="#杂项" class="headerlink" title="杂项"></a>杂项</h1><h2 id="附加资源"><a href="#附加资源" class="headerlink" title="附加资源"></a>附加资源</h2><h2 id="Vim-配置集合"><a href="#Vim-配置集合" class="headerlink" title="Vim 配置集合"></a>Vim 配置集合</h2><h2 id="内置插件"><a href="#内置插件" class="headerlink" title="内置插件"></a>内置插件</h2><h2 id="将-Control-映射到-CapsLock"><a href="#将-Control-映射到-CapsLock" class="headerlink" title="将 Control 映射到 CapsLock"></a>将 Control 映射到 CapsLock</h2><h2 id="复活节彩蛋"><a href="#复活节彩蛋" class="headerlink" title="复活节彩蛋"></a>复活节彩蛋</h2><h2 id="为何使用-hjkl"><a href="#为何使用-hjkl" class="headerlink" title="为何使用 hjkl"></a>为何使用 hjkl</h2><h2 id="常见问题"><a href="#常见问题" class="headerlink" title="常见问题"></a>常见问题</h2><h3 id="编辑小文件时很慢"><a href="#编辑小文件时很慢" class="headerlink" title="编辑小文件时很慢"></a>编辑小文件时很慢</h3><p>有两个因素对性能影响非常大：</p><ol><li><p>过于复杂的 <strong>正则表达式</strong> 。尤其是 Ruby 的语法文件，以前会造成性能下降。（见<a href="#debugging-syntax-files">调试语法文件</a>）</p></li><li><p><strong>屏幕重绘</strong> 。有一些功能会强制重绘所有行。</p></li></ol><table><thead><tr><th>典型肇事者</th><th>原因</th><th>解决方案</th></tr></thead><tbody><tr><td><code>:set cursorline</code></td><td>会导致所有行重绘</td><td><code>:set nocursorline</code></td></tr><tr><td><code>:set cursorcolumn</code></td><td>会导致所有行重绘</td><td><code>:set nocursorcolumn</code></td></tr><tr><td><code>:set relativenumber</code></td><td>会导致所有行重绘</td><td><code>:set norelativenumber</code></td></tr><tr><td><code>:set foldmethod=syntax</code></td><td>如果语法文件已经很慢了，这只会变得更慢</td><td><code>:set foldmethod=manual</code>，<code>:set foldmethod=marker</code> 或者使用<a href="https://github.com/Konfekt/FastFold" target="_blank" rel="noopener">快速折叠</a>插件</td></tr><tr><td><code>:set synmaxcol=3000</code></td><td>由于内部表示法，Vim 处理比较长的行时会有问题。让它高亮到 3000 列……</td><td><code>:set synmaxcol=200</code></td></tr><tr><td>matchparen.vim</td><td>Vim 默认加载的插件，用正则表达式查找配对的括号</td><td>禁用插件：<code>:h matchparen</code></td></tr></tbody></table><p><strong>注意</strong>：只有在你真正遇到性能问题的时候才需要做上面的调整。在大多数情况下使用上面提到的选项是完全没有问题的。</p><h3 id="编辑大文件的时候很慢"><a href="#编辑大文件的时候很慢" class="headerlink" title="编辑大文件的时候很慢"></a>编辑大文件的时候很慢</h3><p>Vim 处理大文件最大的问题就是它会一次性读取整个文件。这么做是由于缓冲区的内部机理导致的（在 <a href="https://groups.google.com/forum/#!topic/vim_dev/oY3i8rqYGD4/discussion" target="_blank" rel="noopener">vim_dev</a> 中讨论）。</p><p>如果只是想查看的话，<code>tail hugefile | vim -</code> 是一个不错的选择。</p><p>如果你能接受没有语法高亮，并且禁用所有插件和设置的话，使用：</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ vim -u NONE -N</span><br></pre></td></tr></table></figure><p>这将会使得跳转变快很多，尤其是省去了基于很耗费资源的正则表达式的语法高亮。你还可以告诉 Vim 不要使用交换文件和 viminfo 文件，以避免由于写这些文件而造成的延时：</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ vim -n -u NONE -i NONE -N</span><br></pre></td></tr></table></figure><p>简而言之，尽量避免使用 Vim 写过大的文件。</p><h3 id="持续粘贴（为什么我每次都要设置-‘paste’-模式）"><a href="#持续粘贴（为什么我每次都要设置-‘paste’-模式）" class="headerlink" title="持续粘贴（为什么我每次都要设置 ‘paste’ 模式）"></a>持续粘贴（为什么我每次都要设置 ‘paste’ 模式）</h3><p>持续粘贴模式让终端模拟器可以区分输入内容与粘贴内容。</p><p>你有没有遇到过往 Vim 里粘贴代码之后被搞的一团糟？</p><p>这在你使用 <code>cmd+v</code>、<code>shirt-insert</code>、<code>middle-click</code> 等进行粘贴的时候才会发生。因为那样的话你只是向终端模拟器扔了一大堆的文本。 Vim 并不知道你刚刚是粘贴的文本，它以为你在飞速的输入。于是它想缩进这些行但是失败了。</p><p>这明显不是个问题，如果你用 Vim 的寄存器粘贴，如：<code>&quot;+p</code> ，这时 Vim 就知道了你在粘贴，就不会导致格式错乱了。</p><p>使用 <code>:set paste</code> 就可以解决这个问题正常进行粘贴。见 <code>:h &#39;paste&#39;</code> 和 <code>:h &#39;pastetoggle&#39;</code> 获取更多信息。</p><p>如果你受够了每次都要设置 <code>&#39;paste&#39;</code> 的话，看看这个能帮你自动设置的插件：<a href="https://github.com/ConradIrwin/vim-bracketed-paste" target="_blank" rel="noopener">bracketed-paste</a>。</p><p><a href="http://cirw.in/blog/bracketed-paste" target="_blank" rel="noopener">点此</a>查看该作者对于这个插件的更多描述。</p><p>Neovim 尝试把这些变得更顺畅，如果终端支持的话，它会自动开启持续粘贴模式，无须再手动进行切换。</p><h3 id="在终端中按-ESC-后有延时"><a href="#在终端中按-ESC-后有延时" class="headerlink" title="在终端中按 ESC 后有延时"></a>在终端中按 ESC 后有延时</h3><p>如果你经常使用命令行，那么肯定要接触 <em>终端模拟器</em> ，如 xterm、gnome-terminal、iTerm2 等等（与实际的<a href="https://en.wikipedia.org/wiki/Computer_terminal" target="_blank" rel="noopener">终端</a>不同）。</p><p>终端模拟器与他们的祖辈一样，使用 <a href="https://zh.wikipedia.org/wiki/%E8%BD%AC%E4%B9%89%E5%BA%8F%E5%88%97" target="_blank" rel="noopener">转义序列</a> （也叫 <em>控制序列</em> ）来控制光标移动、改变文本颜色等。转义序列就是以转义字符开头的 ASCII 字符串（用<a href="https://zh.wikipedia.org/wiki/%E8%84%B1%E5%AD%97%E7%AC%A6%E8%A1%A8%E7%A4%BA%E6%B3%95" target="_blank" rel="noopener">脱字符表示法</a>表示成 <code>^[</code> ）。当遇到这样的字符串后，终端模拟器会从<a href="https://en.wikipedia.org/wiki/Terminfo" target="_blank" rel="noopener">终端信息</a>数据库中查找对应的动作。</p><p>为了使用问题更加清晰，我会先来解释一下什么是映射超时。在映射存在歧义的时候就会产生映射超时：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">:<span class="keyword">nnoremap</span> ,<span class="keyword">a</span> :<span class="keyword">echo</span> <span class="string">'foo'</span><span class="symbol">&lt;cr&gt;</span></span><br><span class="line">:<span class="keyword">nnoremap</span> ,<span class="keyword">ab</span> :<span class="keyword">echo</span> <span class="string">'bar'</span><span class="symbol">&lt;cr&gt;</span></span><br></pre></td></tr></table></figure><p>上面的例子中两个映射都能正常工作，但是当输入 <code>,a</code> 之后，Vim 会延时1秒，因为它要确认用户是否还要输入那个 <code>b</code>。</p><p>转义序列会产生同样的问题：</p><ul><li><code>&lt;esc&gt;</code> 作为返回普通模式或取消某个动作的按键而被大量使用</li><li>光标键使用转义序列进行的编码</li><li>Vim 期望 <kbd>Alt</kbd> （也叫作 <em>Mate Key</em> ）会发送一个正确的 8-bit 编码的高位，但是许多终端模拟器并不支持这个（也可能默认没有启用），而只是发送一个转义序列作为代替。</li></ul><p>你可以这样测试上面所提到的事情： <code>vim -u NONE -N</code> 然后输入 <code>i&lt;c-v&gt;&lt;left&gt;</code> ，你会看到一个以 <code>^[</code> 开头的字符串，表明这是一个转义序列，<code>^[</code> 就是转义字符。</p><p>简而言之，Vim 在区分录入的 <code>&lt;esc&gt;</code> 和转义序列的时候需要一定的时间。</p><p>默认情况下，Vim 用 <code>:set timeout timeoutlen=1000</code>，就是说它会用 1 秒的时间来区分有歧义的映射 _以及_ 按键编码。这对于映射来说是一个比较合理的值，但是你可以自行定义按键延时的长短，这是解决该问题最根本的办法：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">set</span> timeout           <span class="comment">" for mappings</span></span><br><span class="line"><span class="keyword">set</span> timeoutlen=<span class="number">1000</span>   <span class="comment">" default value</span></span><br><span class="line"><span class="keyword">set</span> ttimeout          <span class="comment">" for key codes</span></span><br><span class="line"><span class="keyword">set</span> ttimeoutlen=<span class="number">10</span>    <span class="comment">" unnoticeable small value</span></span><br></pre></td></tr></table></figure><p>在 <code>:h ttimeout</code> 里你可以找到一个关于这些选项之间关系的小表格。</p><p>而如果你在 tmux 中使用 Vim 的话，别忘了把下面的配置加入到你的 <code>~/.tmux.conf</code>文件中：</p><pre><code>set -sg escape-time 0</code></pre><h3 id="无法重复函数中执行的搜索"><a href="#无法重复函数中执行的搜索" class="headerlink" title="无法重复函数中执行的搜索"></a>无法重复函数中执行的搜索</h3><ul><li>在命令中的搜索（<code>/</code>、<code>:substitute</code> 等）内容会改变“上次使用的搜索内容”。（它保存在<code>/</code>寄存器中，用 <code>:echo @/</code> 可以输出它里面的内容）</li><li>简单的文本变化可以通过 <code>.</code> 重做。（它保存在 <code>.</code> 寄存器，用 <code>:echo @.</code> 可以输出它的内容）</li></ul><p>而在你在函数中进行这些操作的时候，一切就会变得不同。因此你不能用 N/n 查找某个函数刚刚查找的内容，也不能重做函数中对文本的修改。</p><p>帮助文档：<code>:h function-search-undo</code>。</p><h2 id="主题列表"><a href="#主题列表" class="headerlink" title="主题列表"></a>主题列表</h2><h2 id="插件列表"><a href="#插件列表" class="headerlink" title="插件列表"></a>插件列表</h2><h1 id="Neovim"><a href="#Neovim" class="headerlink" title="Neovim"></a>Neovim</h1><h2 id="加入我们"><a href="#加入我们" class="headerlink" title="加入我们"></a>加入我们</h2><p>可以协助我们核对翻译，或者从<a href="CONTRIBUTING.md">章节列表</a>中认领章节进行翻译。</p><h2 id="致谢："><a href="#致谢：" class="headerlink" title="致谢："></a>致谢：</h2><ul><li><a href="https://github.com/LCTT" target="_blank" rel="noopener">Linux 中国翻译组</a></li><li><a href="https://github.com/xitu/gold-miner" target="_blank" rel="noopener">掘金翻译计划</a></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;ul&gt;
&lt;li&gt;原文地址：&lt;a href=&quot;https://github.com/mhinz/vim-galore&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/mhinz/vim-galore&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;原文作者：Marco Hinz&lt;/li&gt;
&lt;li&gt;本文地址：&lt;a href=&quot;https://github.com/wsdjeg/vim-galore-zh_cn&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/wsdjeg/vim-galore-zh_cn&lt;/a&gt;&lt;br&gt;&lt;strong&gt;注:转载请保留以上信息&lt;/strong&gt;
    
    </summary>
    
    
      <category term="vim" scheme="https://www.07sec.com/tags/vim/"/>
    
  </entry>
  
  <entry>
    <title>LAMP环境搭建</title>
    <link href="https://www.07sec.com/2017/05/19/LAMP/"/>
    <id>https://www.07sec.com/2017/05/19/LAMP/</id>
    <published>2017-05-19T06:43:13.000Z</published>
    <updated>2022-04-03T10:30:23.734Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>Linux+Apache+Mysql/MariaDB+Perl/PHP/Python一组常用来搭建动态网站或者服务器的开源软件，本身都是各自独立的程序，但是因为常被放在一起使用，拥有了越来越高的兼容度，共同组成了一个强大的Web应用程序平台。</p></blockquote><p>——摘自百度百科</p><h3 id="软件安装"><a href="#软件安装" class="headerlink" title="软件安装"></a>软件安装</h3><p>Arch Linux安装LAMP非常简单，所有的包官方源里都有，所以：</p><pre><code># pacman -S apache php php-apache mariadb libmariadbclient mariadb-clients </code></pre><p>一条命令就将所有需要的软件直接安装好。（ps.Arch Linux 选择的 MySQL 实现被称为 MariaDB。）<br><a id="more"></a></p><h3 id="启动PHP"><a href="#启动PHP" class="headerlink" title="启动PHP"></a>启动PHP</h3><h4 id="配置PHP"><a href="#配置PHP" class="headerlink" title="配置PHP"></a>配置PHP</h4><p>&emsp;&emsp;首先，我们来配置PHP。</p><p>&emsp;&emsp;php-apache 中包含的 libphp7.so 不支持 mod_mpm_event，仅支持 mod_mpm_prefork。所以需要在 /etc/httpd/conf/httpd.conf 中注释掉:</p><pre><code>#LoadModule mpm_event_module modules/mod_mpm_event.so</code></pre><p>&emsp;&emsp;并取消下面行的注释:</p><pre><code>LoadModule mpm_prefork_module modules/mod_mpm_prefork.so</code></pre><p>&emsp;&emsp;此外，将这一行放在LoadModule列表中 LoadModule dir_module modules/mod_dir.so 之后的任意地方：</p><pre><code>LoadModule php7_module modules/libphp7.so</code></pre><p>&emsp;&emsp;将这一行放到Include列表的末尾：</p><pre><code>Include conf/extra/php7_module.conf</code></pre><h4 id="测试PHP"><a href="#测试PHP" class="headerlink" title="测试PHP"></a>测试PHP</h4><p>&emsp;&emsp;要测试PHP，在 apache 文档根目录中创建test.php文件，在其中写入：</p><pre><code>&lt;?php phpinfo(); ?&gt;</code></pre><p>&emsp;&emsp;然后访问启动apache服务，访问 <a href="http://localhost/test.php。若成功启动的话，应如下图所示：" target="_blank" rel="noopener">http://localhost/test.php。若成功启动的话，应如下图所示：</a></p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://poo1.club/img/591edcc26ad0e.png" alt="PHP" title="">                </div>                <div class="image-caption">PHP</div>            </figure><h3 id="启动MySQL"><a href="#启动MySQL" class="headerlink" title="启动MySQL"></a>启动MySQL</h3><h4 id="初始化MySQL"><a href="#初始化MySQL" class="headerlink" title="初始化MySQL"></a>初始化MySQL</h4><p>&emsp;&emsp;首先，我们必须必须运行下面这条命令：</p><pre><code># mysql_install_db --user=mysql --basedir=/usr --datadir=/var/lib/mysql</code></pre><p>&emsp;&emsp;启动 mysqld 守护进程，运行安装脚本，然后重新启动守护进程：</p><pre><code># systemctl start mysqld# mysql_secure_installation# systemctl restart mysqld</code></pre><p>&emsp;&emsp;安装脚本的内容是配置数据库root密码和删除匿名权限还有远程权限之类的，大家各凭所需去选择就行。</p><h4 id="配置MySQL"><a href="#配置MySQL" class="headerlink" title="配置MySQL"></a>配置MySQL</h4><p>&emsp;&emsp;运行完脚本后便可以使用设置好的root账号登陆MySQL：</p><pre><code># mysql -p -u root</code></pre><p>&emsp;&emsp;下面直接改下/etc/php/php.ini，取消这两行的注释：</p><pre><code>extension=pdo_mysql.soextension=mysqli.so</code></pre><p>&emsp;&emsp;本次搭建便已经完成了,这时再重启下httpd.service 服务便可。</p><p>&emsp;&emsp;下面再额外介绍下MySQL的其他配置。</p><h4 id="创建用户"><a href="#创建用户" class="headerlink" title="创建用户"></a>创建用户</h4><p>&emsp;&emsp;以下是创建一个密码为’some_pass’的’monty’用户的示例，并赋予 mydb 完全操作权限：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://poo1.club/img/591ee3307f03c.png" alt="Mydb" title="">                </div>                <div class="image-caption">Mydb</div>            </figure></p><h4 id="禁用远程访问"><a href="#禁用远程访问" class="headerlink" title="禁用远程访问"></a>禁用远程访问</h4><p>&emsp;&emsp;MySQL 服务器默认可从网络访问。如果只有本机需要 MySQL，可以通过不监听 TCP 端口 3306 来增强安全性。要拒绝远程连接，取消注释 /etc/mysql/my.cnf 中以下这行：</p><pre><code>skip-networking</code></pre><h4 id="为数据库使用-UTF-8-编码"><a href="#为数据库使用-UTF-8-编码" class="headerlink" title="为数据库使用 UTF-8 编码"></a>为数据库使用 UTF-8 编码</h4><p>&emsp;&emsp;在 /etc/mysql/my.cnf 的 mysqld 下, 添加:</p><pre><code>[mysqld]init_connect                = &apos;SET collation_connection = utf8_general_ci,NAMES utf8&apos;collation_server            = utf8_general_cicharacter_set_client        = utf8character_set_server        = utf8</code></pre><p><br><br></p><h3 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h3><p>&emsp;&emsp;额😂，还是不滥竽充数了，Arch 的Wiki实在是太全了，一开始是打算参考Wiki写这篇博客，到后面几乎是直接copy了。</p><p>&emsp;&emsp;这篇就此结束吧，回头应该还会补上LNMP搭建，另外再推荐下官方Wiki，比我这个要全并且直观多了，Wiki不愧是Archer的一大财富。</p><p><a href="https://wiki.archlinux.org/index.php/Apache_HTTP_Server" target="_blank" rel="noopener">Apache</a></p><p><a href="https://wiki.archlinux.org/index.php/PHP" target="_blank" rel="noopener">PHP</a></p><p><a href="https://wiki.archlinux.org/index.php/MySQL" target="_blank" rel="noopener">MySQL</a></p>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;Linux+Apache+Mysql/MariaDB+Perl/PHP/Python一组常用来搭建动态网站或者服务器的开源软件，本身都是各自独立的程序，但是因为常被放在一起使用，拥有了越来越高的兼容度，共同组成了一个强大的Web应用程序平台。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;——摘自百度百科&lt;/p&gt;
&lt;h3 id=&quot;软件安装&quot;&gt;&lt;a href=&quot;#软件安装&quot; class=&quot;headerlink&quot; title=&quot;软件安装&quot;&gt;&lt;/a&gt;软件安装&lt;/h3&gt;&lt;p&gt;Arch Linux安装LAMP非常简单，所有的包官方源里都有，所以：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# pacman -S apache php php-apache mariadb libmariadbclient mariadb-clients 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;一条命令就将所有需要的软件直接安装好。（ps.Arch Linux 选择的 MySQL 实现被称为 MariaDB。）&lt;br&gt;
    
    </summary>
    
    
      <category term="Apache" scheme="https://www.07sec.com/tags/Apache/"/>
    
      <category term="Mariadb" scheme="https://www.07sec.com/tags/Mariadb/"/>
    
      <category term="PHP" scheme="https://www.07sec.com/tags/PHP/"/>
    
      <category term="Archlinux" scheme="https://www.07sec.com/tags/Archlinux/"/>
    
  </entry>
  
  <entry>
    <title>用git服务器和hexo搭建博客</title>
    <link href="https://www.07sec.com/2017/05/15/git-and-hexo/"/>
    <id>https://www.07sec.com/2017/05/15/git-and-hexo/</id>
    <published>2017-05-15T06:43:13.000Z</published>
    <updated>2022-04-03T03:28:00.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="Git安装"><a href="#Git安装" class="headerlink" title="Git安装"></a>Git安装</h2><p>  git 安装非常简单，几乎所有的发行版都能在官方源找到，直接用包管理器下载即可。由于我的 vps 使用的 debian ，所以：</p><pre><code>#apt install git </code></pre><h2 id="Git使用"><a href="#Git使用" class="headerlink" title="Git使用"></a>Git使用</h2><ol><li><p>Git仓库初始化</p><p> git 的初始化仓库命令是 git init，git 服务器一般都会带上 –bare 参数，这样不会在服务器上生成工作目录，不过由于我的需求是通过 git 同步 hexo 站点，所以：</p><pre><code>#git init weibo</code></pre><p> 当然，在此之前还要创建个 git 用户，最好把仓库放在git用户有权限的目录下或者用root创建目录，再 chown 给 git 用户。</p><a id="more"></a></li><li><p>Git同步</p><p> 我的需求是 git+nginx+hexo ，完成本地编写网页，再通过 git 来 push 到 nginx 目录下。所以上一步的初始化工作就默认是在 nginx 的目录下完成的。</p><p> 首先我们要修改服务器上的两个文件，一个是 .git/config ，因为默认的配置是不允许 push 操作的，这和我们的需求不同，需要添加如下代码：</p><pre><code>[receive]denyCurrentBranch = ignore</code></pre><p> 解决了不能 push 的问题后，还要解决一个不能同步的问题，因为git服务器的主要作用还是共享数据，所以本地工作目录的文件在别的设备 push 上来后并不会即时同步，必须要手动输入 git reset –hard 才能同步，为了让 git 自动同步 push 内容到 work directory 这里就要用到 git 的 hooks 功能，也就是钩子。</p><p> 仓库的 .git 目录下有一个 hooks 目录，在仓库数据遭遇改变时，hooks下的脚本就会自动执行。所以我们要在这个目录下创建一个新的脚本：</p><pre><code>root@vultr ~www/html/weibo (git)-[master] # cat .git/hooks/post-receive #!/bin/bashgit --work-tree=/var/www/html/weibo checkout -f</code></pre><p> 然后再确定此文件归属 git 用户，还要给予他可执行权限：</p><pre><code># chown git:git post-receive# chmod +x post-receive</code></pre><p> OK了，现在要开始在本地搭建hexo站点，这个非常简单，暂不赘述。搭建完后，使用 hexo g 生成静态文件，再进入静态文件目录 clone 下服务端的仓库：</p><pre><code># git clone git@sieke.lt:/var/www/html/weibo</code></pre><p> 接下来就很简单了，把hexo生成的静态页面再 push 上远程仓库，hooks会自动让仓库的工作目录自动同步 push 内容，一个 git+nginx+hexo 的组合就这么完成了。<br> 最后顺便说一下 push 过程(ps.以readme.txt为例.)：</p><pre><code># git add readme.txt# git commit -m &quot;此处填写改动声明。&quot;# git push origin master</code></pre></li></ol><h2 id="happy-´▽-ʃ♡ƪ"><a href="#happy-´▽-ʃ♡ƪ" class="headerlink" title="happy(´▽`ʃ♡ƪ)"></a>happy(´▽`ʃ♡ƪ)</h2>]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;Git安装&quot;&gt;&lt;a href=&quot;#Git安装&quot; class=&quot;headerlink&quot; title=&quot;Git安装&quot;&gt;&lt;/a&gt;Git安装&lt;/h2&gt;&lt;p&gt;  git 安装非常简单，几乎所有的发行版都能在官方源找到，直接用包管理器下载即可。由于我的 vps 使用的 debian ，所以：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#apt install git 
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;Git使用&quot;&gt;&lt;a href=&quot;#Git使用&quot; class=&quot;headerlink&quot; title=&quot;Git使用&quot;&gt;&lt;/a&gt;Git使用&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Git仓库初始化&lt;/p&gt;
&lt;p&gt; git 的初始化仓库命令是 git init，git 服务器一般都会带上 –bare 参数，这样不会在服务器上生成工作目录，不过由于我的需求是通过 git 同步 hexo 站点，所以：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#git init weibo
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt; 当然，在此之前还要创建个 git 用户，最好把仓库放在git用户有权限的目录下或者用root创建目录，再 chown 给 git 用户。&lt;/p&gt;
    
    </summary>
    
    
      <category term="git" scheme="https://www.07sec.com/tags/git/"/>
    
      <category term="hexo" scheme="https://www.07sec.com/tags/hexo/"/>
    
      <category term="nginx" scheme="https://www.07sec.com/tags/nginx/"/>
    
  </entry>
  
</feed>
