赞
踩
shell 脚本最常见的一个用途就是处理文本文件。检查日志文件、读取配置文件、处理数据元素,shell 脚本可以帮助我们将文本文件中各种数据的日常处理任务自动化。
sed 编辑器被称作流编辑器,和普通的交互式编辑器相反。在交互式文本编辑器中(比如 vim),你可以用键盘命令来交互式地插入、删除或替换数据中的文本。流编辑器则会在编辑器处理数据之前基于预先提供的一组规则来编辑数据流。
sed 编辑器可以根据命令来处理数据流中的数据,这些命令要么从命令行中输入,要么存储在一个命令文本文件中。sed 会执行下列操作:
sed 命令的格式如下:
sed options script file
选项允许修改 sed 命令的行为,可以使用的选项如下表所示:
选项 | 描述 |
---|---|
-e script | 在处理输入时,将 script 中指定的命令添加到已有的命令中 |
-f file | 在处理输入时,将 file 中指定的命令添加到已有的命令中 |
-n | 不产生命令输出,使用 print 命令来完成输出 |
script 参数制定了应用于流数据上的单个命令。如果需要用多个命令,要么使用 -e 选项在命令行中指定,要么使用 -f 选项在单独的文件中指定。
默认勤快下,sed 编辑器会将指定的命令应用到 STDIN 输入流上,这样可以将数据通过管道直接输入 sed 编辑器处理。
在本例中,sed 编辑器使用 s 命令,将斜线间指定的第二个文本字符串(big test)来替换第一个文本字符串(test)。
在面对大量数据时,sed 的处理效率依旧高效,但是需要注意的时 sed 编辑器并不会修改文本文件的数据,它只会讲修改后的数据发送到 STDOUT。
2. 在命令行使用多个编辑器命令
在 sed 命令行上执行多个命令时,只需要使用 -e 选项就可以了。但是命令之间必须用分号隔开,并且在命令行末尾和分号之间不能有空格。
3. 从文件中读取编辑器命令
如果有大量的 sed 命令,可以将它们放进一个单独的文件,之后在 sed 命令中使用 -f 选项来指定文件。这种情况下,不用再每条命令后加上一个分号,sed 编辑器知道每行都是一条单独的命令。
Tips:为了避免将 sed 编辑器脚本文件和 shell 脚本文件搞混,可以使用 .sed 作为 sed 脚本文件的扩展名。
gawk 程序是 Unix 中的原始 awk 程序的 GNU 版本。gawk 编程语言中,可以做下面的事情:
gawk 程序的报告生成能力通常用来从大文本文件中提取数据元素,并将它们格式化成可读的报告。例如,格式化日志文件,gawk 程序可以从日志文件中过滤出需要的数据元素,然后将其格式化。
gawk 程序的基本格式如下:
gawk options program file
gawk 可用选项如下:
选项 | 描述 |
---|---|
-F fs | 指定行中划分数据字段的字段分隔符 |
-f file | 从指定的文件中读取程序 |
-v var=value | 定义 gawk 程序中的一个变量及其默认值 |
-mf N | 指定要处理的数据文件中的最大字段数 |
-mr N | 指定数据文件中的最大数据行数 |
-W keyword | 指定 gawk 的兼容模式或警告等级 |
gawk 程序脚本用一对花括号来定义,由于 gawk 命令行假定脚本是单个文本字符串,还必须将脚本放到单引号中。
该程序会将文本打印到 STDOUT 中,由于 gawk 程序会从 STDIN 接收数据,在运行时,该程序会等待从 STDIN 输入的文本,然后再进行输出。要结束这个 gawk 程序,需要通过 Ctrl+D 组合键在 bash 中产生一个 EOF 字符。
gawk 的主要特性之一是其处理文本文件中数据的能力。它会自动给一行中的每个数据元素分配一个变量。默认情况下,gawk 会将如下变量分配给它在文本行中发现的数据字段:
在文本行中,每个数据字段都是通过字段分隔符划分的。gawk 在读取一行文本时,会用于定义的字段分隔符划分每个数据字段。gawk 中默认的字段分隔符时任意的空白字符(空格或制表符)。
也可以通过 -F 选项来指定其他字段分隔符。
在 gawk 中使用多条命令,只要在命令直接放个分号即可。
也可以使用次提示符一次一行的输入脚本命令,需要结束程序时,只需按下 Ctrl+D 组合键。
gawk 编辑器也允许将程序存储到文件中,然后再命令行中引用。如果要在程序文件中指定多条命令,只需要一条命令放一行即可,不需要使用分号。
注意,gawk 程序在引用变量值时,并未向 shell 脚本一样使用美元符。
gawk 允许指定程序脚本何时运行。默认情况下,gawk 会从输入中读取一行文本,然后针对该行的数据执行程序脚本。但有时可能需要在处理数据前运行脚本,比如为报告创建标题。BEGIN 关键字会强制 gawk 在读取数据前执行 BEGIN 关键字后指定的程序脚本。
在 gawk 执行了 BEGIN 脚本后,它会用第二段脚本来处理文件数据。两端脚本仍然被认为是 gawk 命令行中的一个文本字符串,需要相应的加上单引号。
END 关键字允许你指定一个程序脚本,gawk 会在读完数据后执行它。
当 gawk 程序打印完文件内容后,它会执行 END 脚本中的命令。这是在处理完所有正常数据后给报告添加页脚的最佳方法。gawk 脚本有一个特殊变量 FS,这是定义字段分隔符的另一种方法,这样就不用依靠脚本用户在命令行选项中定义字段分隔符了。
默认情况下,替换命令 s 只替换每行中出现的第一处。
要让替换命令能够替换一行中不同地方出现的文本必须使用替换标记。替换标记会在替换命令字符串之后设置:
s/pattern/replacement/flags
有四种可用的替换标记:
2. 替换字符
在替换文件中的路径时,由于会遇到正斜线,所以会比较麻烦:
sed 's/\/bin\/bash/\/bin\/csh/' /etc/passwd
由于正斜线通常用作字符串分隔符,因而如果它出现在了模式文本种的话,必须用反斜线来转义。为了解决这个问题,sed 编辑器允许选择其他字符来作为替换命令种的字符串分隔符:
sed 's!/bin/bash!/bin/cah!' /etc/passwd
这里,感叹号被用作字符串分隔符,路径名就更容易阅读和理解了。
默认情况下,sed 编辑器种使用的命令会作用于文本数据的所有行。如果指向将命令作用于特定行,需要使用行寻址。寻址有两种形式:以数字形式表示行区间;用文本模式来过滤行。其格式有如下两种:
# 格式一
[address]command
# 格式二
address {
command1
command2
}
sed 编辑器会将文本流种的第一行编号为 1,然后继续按顺序为接下来的行分配行号。在命令种指定的地址可以是单个行号,或是用起始行号、逗号以及结尾行号指定的一定区间范围内的行号,如果详见命令作用到末尾行可以用特殊地址 —— 美元符代替。
2. 使用文本模式过滤器
sed 编辑器允许指定文本模式来过处命令要作用的行,其格式如下:
/pattern/command
必须用正斜线将要指定的 pattern 封装起来,sed 编辑器会将该命令作用到包含指定文本模式的行上。
3. 命令组合
如果需要在单行上执行多条命令,可以用花括号将多条命令组合在一起。sed 编辑器会处理地址行处列出的每条命令。
sed 编辑器会将所有命令作用到该地址区间内的所有行上。
删除命令 d 会伤处匹配模式指定寻址模式的所有行。如果你没有加入寻址模式的话,它会将所有文本行删除。
说明:sed 编辑器不会修改原始文件。删除的行只是从 sed 编辑器的输出中消失了,原始文件没有任何改变。
也可以使用两个文本模式来删除某个区间内的行。指定的第一个模式会打开行删除功能,第二个模式会关闭行删除功能。sed 编辑器会删除两个指定行之间的所有行。
但是如果 sed 编辑器只匹配到了开始模式,没有匹配到结束模式,它就会将后续所有数据全部删除。
着两个命令不能在单个命令行上使用,必须指定是要将行擦汗如还是附加到另一个行,其格式如下:
sed '[address]command\
new line'
new line 中的文本将会出现在 sed 编辑器输出中指定的位置。一旦输入了结尾的但银行,shell 就会执行该命令。
修改行跟插入和附加命令的工作机制一样,你必须在 sed 命令中单独指定新行。
转换命令 y 是唯一可以处理单个字符的 sed 编辑器命令,其格式如下:
[address]y/inchars/outchars/
转换命令会对 inchars 和 outchars 值进行一对一的印射。inchars 中的第一个字符会被转换为 outchars 中的第一个字符,第二个字符会被转换成 outchars 中的第二个字符,这个过程会一直持续到处理完指定的字符。inchars 和 outchars 的长度必须相同,否则 sed 编译器会产生一条错误消息。
转换命令是一个全局命令,也就是说,它会将文本行中找到的所有指定字符自动进行转换,而不会考虑它们的位置。
有 3 个命令可以用来打印数据流中的信息:
跟替换命令中 p 标记类似,p 命令可以打印 sed 编辑器输出中的一行,它所做的就是打印已有的数据文本。打印命令最常见的用法就是配合 -n 选项(禁止输出),打印包含匹配文本模式的行。
2. 打印行号
等号命令会打印行在数据流中的当前行号,行号有数据流中的换行符决定,每次数据流中出现一个换行符,sed 编辑器会认为一行文本结束了。
3. 列出行
列出命令可以打印数据流中的文本和不可打印的 ASCII 字符。任何不可打印的字符要么在其八进制前加一个反斜线,要么使用标准 C 风格的命名法。
其中制表符的位置使用 \t 表示,行尾的美元符用来表示换行符。如果数据流包含了转义字符,列出命令会在必要时使用八进制码来表示。
w 命令用来向文件写入行,其格式如下:
[address]w filename
filename 可以是绝对路径也可以是相对路径,只要运行 sed 编辑器的用户有文件的写权限。地址可以是 sed 中支持的任意类型的寻址方式,例如单个行号、文本模式、行区间。
读取命令允许你讲一个独立文件中的数据插入到数据流中,其格式为:
[address]r filename
在读取命令中只能指定单独的一个行号或文本模式地址,sed 编辑器会将文件中的文本插入到指定地址之后。如果要在数据流的末尾添加文本,只需用美元符地址就可以了。
读取命令的一个用法是配合删除命令使用:利用另一个文件中的数据来替换文件中的占位文本。
sed 编辑器包含了三个可用来处理多行文本的特殊命令
-N:将数据流中的下一行加进来创建一个多行组来处理。
-D:删除多行组中的一行。
-P:打印多行组中的一行。
小写的 n 命令会告诉 sed 编辑器移动到数据流中的下一文本行,而不用重新回到命令的最开始在执行一边。通常 sed 编辑器在移动到数据流中的下一文本行之前,会在当前行上执行完所有定义好的命令。
参考下面的例子,我们的目标是删除首行之后的空白行,留下最后一行之前的空白行。
sed 编辑器执行完命令脚本后,会从数据流中读取下一行文本,并从头开始执行命令脚本。
单行 next 命令会将数据流中的下一文本行移动到 sed 编辑器的工作空间(称为模式空间),多行版本的 next 命令(大写 N)会将下一文本行添加到模式空间中已有的文本后。
这样的作用是将两个文本行合并到同一个模式空间中,文本行仍然用换行符分隔,但 sed 编辑器现在会将两行文本当成一行来处理。
下述方法体现了,短语分散在两行中时,替换命令以及 N 命令的配合。
sed 编辑器通过 d 命令来删除模式空间中的当前行,但是呵 N 命令一起使用时,单行命令会将两行同时删除。
sed 编辑器提供了多行删除命令 D,它只删除模式空间中的第一行,该命令会删除到换行符(含换行符)为止的所有字符。
如果需要删掉目标字符串所在行的前一文本行,该方法十分有用。
多行打印命令 P 沿用了同样的方法,它只打印多行模式空间中的第一行,这包括模式空间中直到换行符为止的所有字符。
模式空间是一块活跃的缓冲区,在 sed 编辑器执行命令时,它会保持待检查的文本。sed 编辑器还有另一块被称为保持空间的缓冲区。在处理模式空间中的某些行时,可以用保持空间来临时保存一些行。下面是 5 条可以用来操作保持空间的命令:
命令 | 描述 |
---|---|
h | 将模式空间复制到保持空间 |
H | 将模式空间附加到保持空间 |
g | 将保持空间复制到模式空间 |
G | 将保持空间附加到模式空间 |
x | 交换模式空间和保持空间的内容 |
通常,在使用 h 或 H 命令将字符串移动到保持空间后,最终还要用 g、G 或 x 命令将保存的字符串移回模式空间。
通过保持空间来回移动文本行,你可以强制让第一个数据行出现在第二个数据行之后。
感叹号命令 ! 用来排除命令,也就是让原本会起作用的命令不起作用。
通过排除命令,我们可以做到将文件内容倒序输出。
通常, sed 编辑器会从脚本的顶部开始,一直执行到脚本的结尾(D 命令是个例外,它会强制 sed 编辑器返回到脚本的顶部,而不读取新的行)。sed 编辑器提供了一个方法来改变命令脚本的执行流程,其结果禹结构化编程类似。
排除命令可以用来排除作用在某行上的命令,实际上 sed 编辑器也提供了一种方法,可以基于地址、地址模式或地址区间排除一整块命令。这允许你只对数据流中的特定行执行一组命令。分支命令 b 的格式如下:
[address]b [label]
address 参数决定了哪些行的数据会触发分支命令。label 参数定义了要跳转到的位置,如果没有 label 参数,跳转命令会跳转到脚本的结尾。
如果不想直接跳到脚本的结尾,可以为分支命令定义一个要跳转到的标签,标签以冒号开始,最多可以是 7 个字符长度。:label2
要指定标签,将它加到 b 命令后即可,使用标签允许你跳过地址匹配出的命令,但腾然执行脚本中的其他命令。
跳转命令指定如果文本行中出现了 first,程序应该跳转到标签为 jump1 的脚本行。如果分支命令的匹配模式没有匹配,sed 编辑器会继续执行脚本中的命令,包括分支标签后的命令。如果某行匹配了分支模式,sed 编辑器就会跳转到带有分支标签的那行。
测试命令 t 也可以用来改变 sed 编辑器脚本的执行流程。测试命令会根据替换命令的结果跳转到某个标签,而不是根据地址进行跳转。如果替换命令成功匹配并替换了一个模式,测试命令就会跳转到指定的标签,如果替换命令未能匹配指定的模式,测试命令就不会跳转。其格式为:
[address]t [label]
如果没有指定标签的化,如果测试成功,sed 会跳转到脚本的结尾。
第一个替换命令会查找模式文本 first,如果匹配了行中的模式,它就会替换文本,而且测试命令会跳过后面的替换命令。如果第一个替换命令未能匹配模式,第二个替换命令就会被执行。
参考下面的例子,如果你想在模式中使用通配符来匹配多个单词。
模式字符串用点号通配符来匹配 at 前面的一个字母。但是用于替代的字符串无法匹配已匹配单词中的通配字符。
& 符号可以用来代表替换命令中匹配的模式,不管模式匹配的是什么样的文本,都可以用 & 符号来代表这段文本。
sed 编辑器用圆括号来定义替换模式中的子模式。可以在替代模式中使用特殊字符来引用每个子模式,代替字符由反斜线和数字组成。sed 编辑器会给第一个子模式分配字符 \1,给第二个子模式分配字符 \2,以此类推。
注意:当在替换命令中使用圆括号时,必须用转义字符将它们标示为分组字符而不是普通的圆括号。
参考下面的例子。
可以将 sed 编辑器命令放到 sehll 包装脚本中,这样就不用每次使用时都重新键入整个脚本。包装脚本充当着 sed 编辑器脚本和命令行之间的中间人角色。
在 shell 脚本中,可以将普通的 shell 变量以及参数和 sed 编辑器脚本一起使用。参考下面的例子。
默认情况下,sed 编辑器会将脚本的结果输出到 STDOUT 上,你可以在 shell 脚本中使用各种标准方法对 sed 编辑器的输出进行重定向。
可以在脚本中用 $() 将 sed 编辑器命令的输出重定向到一个变量中,以备后用。
在使用普通的阶乘计算脚本后,脚本的结果会被作为 sed 编辑器脚本的输入,它会给结果加上逗号,然后存入变量 result。最后使用 echo 语句将结束显示在 STDOUT 上。
数据字段变量允许使用美元符和字段在该记录中的位置值来引用记录对应的字段。因此,要引用记录中国的第一个数据字段,就用变量 $1;要引用第二个字段,就用 $2,以此类推。
数据字段是由字段分隔符来划定的。默认情况下,字段分隔符是一个空白字符,也就是空格或制表符。可以通过 -F 选项或者特殊的内置变量 FS 来更改字段分隔符。
下表列出了一下内置变量:
变量 | 描述 |
---|---|
FIELDWIDTHS | 由空格分隔的一列数字,定义了每个数据字段的确切宽度 |
FS | 输入字段分隔符 |
RS | 输入记录分隔符 |
OFS | 输出字段分隔符 |
ORS | 输出记录分隔符 |
默认情况下,gawk 将 OFS 设置成一个空格。下面是一个简单的例子。
FS 会修改字段分隔符,print 命令会自动将 OFS 变量的值放置在输出中的每个字段间。
FIELDWIDTHS 变量允许你不依靠字段分隔符来读取记录。在一些应用程序中,数据并没有使用字段分隔符,而是被访者在了记录中的特定列。这种情况下,必须设定 FIELDWIDTHS 变量来匹配数据在记录中的位置。一旦设置了 FIELDWIDTHS 变量,gawk 就会忽略 FS 变量,并根据提供的快读来计算字段。
警告:一旦设定了 FIELDWIDTHS 变量的值,就不能再改变了。这种方法并不适用于变长的字段。
变量 RS 和 ORS 定义了 gawk 程序如何处理数据流中的记录。默认情况下,gawk 将 RS 和 ORS 设为换行符。默认的 RS 值表明,输入数据流中的每行新文本就是一条新记录。
gawk 还提供了其他的一些内置变量可以帮助你了解数据发生了什么变化,并提取 shell 环境的信息。
变量 | 描述 |
---|---|
ARGC | 当前命令行参数的个数 |
ARGIND | 当前文件在 ARGV 中的位置 |
ARGV | 包含命令行参数的数组 |
CONVFMT | 数字的转换格式,默认值为 %.6g |
ENVIRON | 当前 shell 环境变量及其值组成的关联数组 |
ERRNO | 当读取或关闭输入文件发生错误时的系统错误号 |
FILENAME | 用作 gawk 输入数据的数据文件的文件名 |
FNR | 当前数据文件中的数据行数 |
IGNORECASE | 设成非零值时,忽略 gawk 命令中出现的字符串的字符大小写 |
NF | 数据文件中的字段总数 |
NR | 已处理的输入记录数 |
OFMT | 数字的输出格式,默认值为 %.6g |
RLENGTH | 由 match 函数所匹配的子字符串的长度 |
RSTART | 由 match 函数所匹配的子字符串的起始位置 |
gawk 自定义变量名可以是任意数目的字母、数字和下划线,但不能以数字开头,且变量名区分大小写。
在 gawk 程序中给变量赋值跟在 shell 脚本中赋值类似,都用赋值语句。
赋值语句不仅可以有文本值,还可以包含数组算是来处理数字值。
通过 gawk 命令行来给程序中的变量赋值,这允许你在正常的代码之外赋值,即时改变变量的值。
这个特性可以让你在不改变脚本代码的情况下就能够改变脚本的行为。使用命令行参数来定义变量值会有一个问题,就是在设置了变量后,这个值在代码的 BEGIN 部分不可用,解决这个问题需要使用 -v 选项。
可以用标准赋值语句来定义数组变量,其格式如下:
var[index] = element
其中 var 时变量名,index 时关联数组的索引时,element 时数组元素值。在引用数组变量时,必须包含索引值来提取相应的数据元素值。
如果要在 gawk 中遍历一个关联数组,可以用 for 语句的一种特殊形式:
for (var in array) {
statements
}
这个 for 语句会在每次循环时将关联数组 array 的下雨给索引值赋给变量 var,然后执行一遍 statements。这个变量中存储的是索引值而非数组元素值。
从关联数组中删除数组索引要使用一个特殊的命令,其格式如下:
delete array[index]
删除命令会从数组中删除关联索引值和相关的数据元素值。
gawk 程序支持多种类型的匹配模式来过滤数据记录,这点和 sed 编辑器大同小异,BEGIN 和 END 关键字就是用来读取数据流之前或之后执行命令的特殊模式。
在使用正则表达式时,正则表达式必须出现在它要控制的程序脚本的左花括号前。
匹配操作符允许将正则表达式限定在记录中的特定数据字段。可以指定匹配操作符、数据字段变量以及要匹配的正则表达式。
$2 ~ /^data 1/
这个表达式会过滤出第二个字段以文本 data 1 开头的所有记录。
gawk 中也可以使用任何常见的数学比较表达式:
也可以对文本数据使用表达式。
gawk 编程语言支持标准的 if-then-else 格式的 if 语句。你必须为 if 语句顶一个一个求值的条件,并将其用圆括号括起来。如果求值条件为 TRUE,if 语句后的语句会执行 ,否则这条语句就会被跳过,并且执行 else 后的语句(如果有的话)。其格式为:
if (condition)
statement1
else
statement2
# 或者
if (condition) statement1; else statement2
如果 if 语句中执行多条语句,就必须用花括号将它们括起来。
while 语句为 gawk 程序提供了一个基本的循环功能,下面是 while 语句的格式:
while (condition) {
statements
}
do-while 语句类似于 while 语句,但会在检查条件语句之前执行一次命令。下面是 do-while 语句的格式:
do {
statements
} while (condition)
for 语句是许多编程语言执行循环的常见方法。gawk 编程语言支持 C 风格的 for 循环:
for (variable assignment; condition; iteration process)
格式化打印命令是 printf,gawk 中的 printf 命令用法类似于 C 语言中的 printf 命令,允许指定具体如何显示数据。其格式为:
printf "format string", var1, var2 ...
format string 是格式化输出的关键,它会用文本元素和格式化指定符来具体指定如何呈现格式化输出。格式化指定符是一种特殊的代码,会指明显示什么类型的变量以及如何显示。gawk 程序会将每个格式化指定符作为占位符,供命令中的变量使用。
格式化指定符格式如下:
%[modifier]control-letter
其中 control-letter 是一个单字符代码,用于指明显示什么类型的数据,而 modifier 则定义了可选的格式化特性。下表列出了可用在格式化指定符中的控制字母。
控制字母 | 描述 |
---|---|
c | 将一个数作为 ASCII 字符显示 |
d | 显示一个整数值 |
i | 显示一个整数值(和 d 一样) |
e | 用科学计数法显示一个数 |
f | 显示一个浮点值 |
g | 用科学计数法或浮点数法显示(选择较短的格式) |
o | 显示一个八进制值 |
s | 显示一个文本字符串 |
x | 显示一个十六禁止值,用小写字母 |
X | 显示一个十六禁止值,用大写字母 |
除了控制字母外,还有 3 种修饰符可以用来进一步控制输出:
gawk 编程语言提供了不少内置函数,可以进行一些常见的数学、字符串以及时间函数运算。
要定义自己的函数,必须使用 function 关键字,同样可以通过 return 语句返回一个值,其格式为:
function name([variables]) {
statements
return value
}
函数名必须能够唯一标识函数。
在定义函数时,它必须出现在所有代码块之前(包括 BEGIN 代码块)。这有助于将行数代码与 gawk 程序其他部分分开。
使用库,只要创建一个含有你的 gawk 程序的文件,然后再命令行上同时指定库文件和程序文件就行了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。