暗无天日

=============>DarkSun的个人博客

Emacs中那些不常用的行操作命令

1 显示并编辑符合条件的行

Emacs内建occur命令,可以让你只显示buffer中匹配某正则表达式的行.

使用 occur 的方式很简单,你只需要执行 M-x occur 然后输入要匹配的正则表达式,occur就会创建一个名为 *Occur* 的buffer,里面列出了所有匹配的行内容以及对应的行数.

通过点击 *Occur* 中的行,能够把光标定位到原buffer相应行的位置,方便你编辑.

执行 occur 后,你可以在全局环境中,通过 M-g M-n 快速跳转到原buffer中下一个匹配行的位置,通过 M-g M-p 快速跳转到原buffer中上一个匹配行的位置.

1.1 显示上下文

有些时候,光显示匹配的行的内容还不够,你可能还需要同时显示上下几行的内容,这时你可以配置变量 list-matching-lines-default-context-lines.

该变量的值为负数表示同时显示匹配行上面几行的内容. 而正数则表示同时显示匹配行上面和下面几行的内容. 默认情况下该变量的值为0表示不显示上下行.

1.2 occur-mode的常用快捷键

*Occur* buffer的major mode为 occur-mode. 它有一些常见的快捷键如下所示:

M-n
跳转到下一个匹配行的位置
M-p
跳转到上一个匹配行的位置
<
跳转到 *occur* buffer的开始位置
>
跳转到 *occur* buffer的结束位置
点击匹配行或在匹配行上按回车
跳转到原buffer中匹配行的位置
g
刷新 *occur* buffer中的搜索结果. 常用于原buffer修改之后.
e
进入occur的编辑状态
C-c C-c
退出occur的编辑状态,并将修改应用到原buffer中
q
退出 *occur* buffer

1.3 编辑occur buffer

*occur* buffer本来是只读的,但是当你按下 e 后会发现buffer会变成可编辑的了.

并且神奇的是,你在 *occur* buffer中修改的变动也会反映到原buffer中去. (注意,只有对匹配行做出的修改会反映到原buffer中去,对上下文行的修改不会改变原buffer)

修改完后,按下 C-c C-c, *occur* 就又变回只读模式了.

1.4 Multi-Occur

若要在多个buffer上同时使用occur,则你需要用 multi-occur. 有两种方式调用 multi-occur:

一种方式是用 M-x multi-occur-in-matching-buffers. 该命令会让你输入一个正则表达式来匹配要在哪些buffer上调用 occur.

另一种方式是直接运行 M-x multi-occur, 该命令则需要你明确地选择在哪几个buffer上调用 occur.

2 删除重复行

选中一个区域后,运行 M-x delete-duplicate-lines 就能删除调该区域中的重复行. 而且它与命令行工具 uniq 不同的是,这些重复行无需预先进行排序.

你还可以通过给 delete-duplicate-lines 提供不同的prefix argument的方式来改变删除重复行的方式:

prefix argument 效果
Without 从上往下扫描重复行,保留最上面的重复行
C-u 从下往上扫描重复行,保留最下面的重复行
C-u C-u 只删除相邻的重复行
C-u C-u C-u 不要删除相邻的重复行

3 删除/保留符合条件的行

若你想根据某个正则表达式来删除匹配的行,那么你可以使用 M-x flush-lines.

选中一个region后,执行 M-x flush-lines,Emacs会提示你输入一个正则表达式,随后整个region中所有匹配该正则表达式的行都会被删除.

若你执行 M-x flush-lines 之前没有选中region,则表示作用于从光标当前位置到buffer结尾这部分的区域.

相应的,如果你想只保留匹配某个正则表达式的行,那么可以使用 M-x keep-lines. 它的用法与 flush-lines 一样,只不过它删除的是所有不匹配该正则表达式的行.

flush-lineskeep-lines 常用于查看日志文件时用于剔除非重要的信息.

4 行排序

4.1 sort-lines

最简单的行排序方法是调用 M-x sort-lines,它会将region内的行按从小到大的顺序进行排序.

若你想按照从大到小的顺序进行排序,则可以给它传递一个prefix argment: C-u M-x sort-lines.

类似的,若你执行 M-x sort-lines 之前没有选中region,则表示作用于从光标当前位置到buffer结尾这部分的区域.

它的作用类似于不带任何参数调用 sort

4.2 sort-fields / sort-numeric-fields

如果你想根据行中第N个域的值来排序,那么你需要用的命令就是 sort-fieldssort-numeric-fields 了.

这两个命令的使用方式是一样的. 都是通过传递一个numeric argument来指定根据哪个域(从1开始计数)的值来进行排序. 例如,要根据第3个域的内容,以数字的方式进行排序,则执行 C-3 sort-numeric-fields.

sort-fieldssort-numeric-fields 只能根据某一个域的值进行排序,而且域与域之间肯定是以空格未做分隔的.

它的作用类似于 sort -kNsort -kN -n

4.3 sort-columns

sort-columns 可以让你指定根据那几列的值进行排序,方法是先mark一个region在执行 M-x sort-columns. 这个region的高指定了要对哪些行进行排序,region的宽则指定了根据那些列的值进行排序.

4.4 sort-regexp-fields

sort-regexp-fields 使用起来很麻烦. 它需要你输入两个正则表达式.

第一个正则表达式叫做RECORD-REGEXP,用来标识一行中的哪些内容将会被重新排序. 只有匹配该表达式的内容会被重排,不匹配该表达式的部分则保持原内容不变.

第二个正则表达式叫做KEY-REGEXP,用来从每行内容中抽取出key的,Emacs就是使用这个key的值来进行排序的.

详细的关于RECORD-REGEXP与KEY-REGEXP的说明请参见 C-h f sort-regexp-fields 的说明

下面是一个取自"mastering-emacs"的例子:

假设你又一个cvs文件内容为

Price,Product
$3.50,Cappuccino
$4.00,Caramel Latte
$2.00,Americano
$2.30,Macchiato

你现在需要让它按照价格进行排序. 那么你执行

M-x sort-regexp-fields
Record: ^\([^,]+\),\([^,]+\)$
Key: \1

注意,由于你想要对整个行的内容都进行排序,因此RECORD-REGEXP需要将这个行的内容都匹配进来.

KEY-REGEXP为 \1 则表示使用RECORD-REGEXP中第1个元组的内容作为key. 你还可以使用 \& 来表示整个RECORD-REGEXP匹配的内容.

最后排序的结果就成了

Price,Product
$2.00,Americano
$2.30,Macchiato
$3.50,Cappuccino
$4.00,Caramel Latte

如果上面例子中RECORD-REGEXP的值改成 ^\([^,]+\),注意,此时RECORD-REGEXP的匹配范围只覆盖了第一个域的部分,则排序的结果会是

$2.00,Cappuccino
$2.30,Caramel Latte
$3.50,Americano
$4.00,Macchiato

也就是说只有第一列排序了,第二列还是保持原顺序不变.

5 行对齐

5.1 align及align-current

一般来说,要对代码进行对齐只需要选中一个region,然后运行 M-x align 就行了. Emacs会自动根据 align-rules-list 中定义的规则自动进行对齐操作.

如果觉得先要选中region太麻烦的话,Emacs提供了一个 M-x align-current 命令. 该命令会先看当前行符合哪个重排规则,然后尝试下一行是否符合该重排规则,若符合该重排规则则进行重排然后再检查下一行,一直到某一行不符合该重排规则为止.

例如: 假设有这么一段C代码,光标在它的第一行位置.

#define bufsize 512
#define pathsize 512
#define xx 51

int a=1;
int ab=2;

执行 M-x align-current 后结果变成

#define bufsize  512
#define pathsize 512
#define xx       51

int a=1;
int ab=2;

可以看到只有第一段代码对齐了.

而先用 C-x h 选中整个buffer后,再运行 align 的结果则是

#define bufsize  512
#define pathsize 512
#define xx       51

int a  = 1;
int ab = 2;

所有的代码都重排了.

5.2 align-regexp

align-regexp允许你自定义自己的对齐方式. 它有两种模式,新手模式和复杂模式.

5.2.1 新手模式

直接运行 align-regexp 会进入新手模式.

在新手模式下,你只需要输入一个表示对齐标识的正则表达式就可以了.

例如下面是一个从"mastering-emacs"中截取的例子:

假设原始文档是这样的

Cappuccino $2.00
Caramel Latte $2.30
Americano $3.50
Macchiato $4.00

我们想要让它按照$对齐,那么可以这样,运行 M-x align-regexp,然后在"Align regexp:"中输入 \$

最终的结果是:

Cappuccino    $2.00
Caramel Latte $2.30
Americano     $3.50
Macchiato     $4.00

5.2.2 复杂模式

当你要对多列进行对齐时,就必须要使用复杂模式了,进入的方式是给它一个prefix argument: C-u M-x align-regexp

在复杂模式下,Emacs会以此要求你输入一个表示对齐标识的正则表达式,且这个正则表达式中必须包含至少一个分组. 一个常见的分组就是 \\(\\s-*\\) 表示任意多个空白字符.

随后Emacs会询问你可以修改第几个分组中的内容来进行对齐.

再然后Emacs问你会询问你要用对齐后两个域之间最少间隔多少个空白,默认是 align-default-spacing 中的值.

最后Emacs会询问你是否重复应用此规则于多列,当需要多列对齐时,往往需要选择 yes

下面还是一个从"mastering emacs"中的例子:

假设有这么段文字

Price,Product,Qty Sold
$2.00,Cappuccino,289
$2.30,Caramel Latte,109
$3.50,Americano,530
$4.00,Macchiato,20

我们想让它按照逗号对齐,由于涉及到多列对齐,因此需要使用 C-u M-x align-regexp 进入复杂模式.

我们要根据逗号进行对齐,且空格应该插入到逗号的后面,因此"Complex align using regexp: "的值应该输入",\(\s-*\)" 其中逗号后面的分组就是插入空格的位置.

由于我们要修改的是表达式中第一个分组的位置,因此"Parenthesis group to modify (justify if negative):" 的值我们输入 1

我们可以让每个对齐列之间分隔的间距大一点,这里"Amount of spacing (or column if negative): " 我们输入 5,表示最少间距是5个空格

最后我们要对齐多个列,因此"Repeat throughout the line: "我们输入 yes.

最终的结果是:

Price,     Product,           Qty Sold
$2.00,     Cappuccino,        289
$2.30,     Caramel Latte,     109
$3.50,     Americano,         530
$4.00,     Macchiato,         20