《Effective sed/AWK Programming》笔
在西单图书大厦闲溜达,发现了一个日本计算机杂志的中文版,《Software Design》,一直有学习Shell的想法,觉得自己在Linux环境下工作的效率简直弱成渣。。。
以下是摘录的笔记(边读边写,结果发现差不多把试读的部分抄了一遍。。):
第1章 sed和awk超级入门
-
为了把多个命令组合起来使用,人们进一步发展了在OS设计之初就有的标准输入输出的想法,从而出现了作为命令组合手段的“管道”结构。
-
标准输入输出是一种在程序被执行时,OS可以自动地分配输入、输出以及错误输出设备的机制。
-
OS在程序启动时会执行一种叫做startup routine的共通的初始化处理。在这个处理中准备好标准输入输出以便于使用。标准状态的分配是这样的:输入设备(标准输入)是键盘,输出设备(标准输出)以及错误输出设备(标准错误输出)是终端。
-
shell和重定向:
- 用户输入命令,系统会解释输入的命令字符串,生成进程,把命令的执行结果显示出来。
- shell的重定向功能:
-
>
变更标准输出的输出位置,如输出位置文件已经存在,那么会先删除文件的所有内容 ,再把新内容写到文件中去。 -
>>
变更标准输出的输出位置,追加文件内容 -
<
变更标准输入的输入方。
-
-
管道是输入输出的想法进一步发展的结果,是为了实现某个命令的标准输出直接作为另一个命令的输入而提供的单向数据流机制。标准shell一般用符号
|
来表示管道。commandA | commandB
就是表示A的标准输出作为B的标准输入来使用。在管道中流通的数据成为“流”是单纯的字节流,并没有像“行“这样的数据分割概念。 -
空管道读入(从管道的输入侧输入数据),直到能有数据读出为止,读入处理都是加锁的。另外,由于输出侧的命令不能从管道读入数据等原因,OS 在内部为管道预留的缓存都满了的情况下,(从管道输出侧读入数据直到缓存为空为止)对管道的写入操作也是加锁的。
-
利用管道吧多个命令组合起来使用时,为了让多个命令都顺场地使用数据,一般只使用由ASCII码构成的文本数据。
-
对标准输入中输入的数据进行加工,并在标准输出中进行输出的命令叫“过滤器”命令。sed和awk也是为了加工数据流而做成的文本加工过滤器命令。
-
sed基本以行为单位来处理输入数据
-
cat '/etc/passwd | sed "s!/bin/bash$!/usr/local/bin/bash!g"
使用sed替换字符串- 最后的
$
表示作为替换对象的/bin/bash处于行尾。 - 因为替换对象的字符串中包含
"/"
,所以没有采用sed的标准记述法"/"
,而是用"!"
- 最后的
-
awk会实现把输入的数据根据字段单位进行分割,如果没有指定分割单位,以输入数据中的空格或者tab为分隔符对数据进行分割。
awk ' BEGIN { FS=":" } { shells[$NF]++; } END { for(i in shells) print i ":" shells[i]; }' /etc/passwd
BEGIN
节是在开始读入输入行之前要执行的处理。用FS=":"
把字段分隔符给为:
。awk用变量NF
来保存输入行的字段数,$NF
可以用来发问最后一个字符段的值。END
节记述的是在所有的输入行读入完成之后要执行的处理。 -
grep这个词原本就是表示一个叫做ed的编辑器的命令文法,它表示把g(Global=把整个文件作为对象)用re(Regular Expression)p(print)出来。
-
通过给LANG环境变量设置适当的值,通知系统和命令可以使用的字符编码。
echo $LANG > en_US.UTF-8
第2章 sed详解及用法
sed的基本形式- 命令:
-
s
: 进行字符串替换; -
d
: 删除; -
p
: 数据输出; -
y
: 替换1个字符; -
w
: 文件输出; -
n
: 文件输入。
-
- s命令:字符串替换:
$ sed -e 's/源字符串/[替换后的字符串]/[标志]' 输入.txt
-
$ sed -e 's/aaa//g' input.txt
-> aaa被删除 - 源字符串可以用正则表达时表示,如果包含"/",可以用""进行转义,或者用其他字符替换。
- [ 替换后的字符串 ]中出现"&"时表示和查找字符串一样。
sed -e 's/aaa/+&+/g' input.txt
-> aaa被替换成+aaa+ - 标志可以指定为:g(Global) / p(print) / w (write):
-
g
把文章整体作为替换范围时使用
$ sed -e 's/bbb/aaa/g' input.txt
$ sed -g -e 's/bbb/aaa/' input.txt
注意-g要在-e前面
两个命令的效果一样 -
p
只表示发生替换的行,不过默认情况下不管是不是发生替换sed都会把输入数据输出到标准输出,如果只加标志"p",会输出两行相同的内容。可以在sed启动时加上"-n"选项来控制标准输出。 -
w
如果想把替换后的结果写到文件中可以使用w标志。
$ sed -e 's/aaa/eee/gw output.txt' input.txt
此时在output.txt只输出匹配的行,如果同时还想输出不匹配的行,使用"w"命令会更简单
$ sed -e 's/aa/eee/g' -e 'w output.txt' input.txt
指定了多个-e脚本
-
-
- y命令:替换1个字符
$ sed -e 'y/abc/xyz/' input.txt
-> a=>x, b=>y, c=>z - d命令: 删除
d命令是删除指定行,并将剩余行进行输出的命令。
$ sed -e '1,2d' input.txt
-> 删除第1,2 行
如果省略地址就表示删除所有行,输出结果边为空;只执行d命令输入文件不会变化。 - 地址
地址列 | 表示的含义 |
---|---|
(未指定) | 所有数据 |
3 | 第三行 |
20,$ | 从第20行开始到最后一行 |
10,5 | 第10行(第2个数字<=第1个数字的情况) |
/^[0-9]/ | 所有以数字开头的行 |
15,/Z$/ | 从第15行开始到以Z结尾的行为止 |
5,10! | 5~10行以外 |
-
指定多个命令
-
命令之间用分号 ; 分割
$ sed -e '2d;s/aaa/eee/g' input.txt
-> 删除第2行,然后,全局替换aaa为eee -
或者,在sed的启动选项中,通过i多个-e脚本
$ sed -e '2d' -e 's/aaa/eee/g' input.txt
-
-
文件输出
- 用
>
重定向到文件 - 用w命令写到文件
- 在sed启动时加上
-i
选项,将处理结果覆盖到原文件中。甚至还可以将处理前的数据作为备份文件保存起来
$ sed -i.bak -e 's/bbb/eee/' input.txt
-->生成备份文件input.txt.bak,替换内容保存在input.txt 中
- 用
-
双引号与单引号
- “, /, $, `, \ 都是特殊字符,在使用sed指定脚本时,为了不作为特殊字符处理,需要用单引号’ 括起来。
- 用双引号可以使用shell变量
sed -e "s/$HOSTNAME/new_hostname/g" /etc/hosts
- 双引号使用特殊字符时需要用\进行转义
-
脚本文件的读取和执行
sed -f 脚本文件 输入文件
如果制定多个-f选项,则按照指定的顺序从文件中读取脚本进行执行。
# sample.sed
1,3s/aaa/eee/g
3d
$ sed -f sample.sed input.txt
-
命令的组合
{ ... }
1, 3{
s/aaa/eee/g
y/abc/xyz/
p
} -
标签/分支处理/循环处理
指定方法 | 所表示的内容 |
---|---|
:label名 | 指定标签,label名在脚本中是唯一的 |
b label名 | 无条件跳转到label名指定的命令进行执行 |
条件 b label名 | 输入行满足条件时,跳转到label名指定的命令进行执行。 如果指定"/模式/b 标签",输入行和模式匹配时,跳转到label名指定的命令进行执行 |
条件 !b label名 | 输入行不满足条件时,跳转到label名指定的命令进行执行。 如果指定"/模式/b 标签",输入行和模式不匹配时,跳转到label名指定的命令进行执行 |
t label名 | 读入输入行执行命令,只有在使用s命令替换成功时才会跳转到label名指定的命令进行执行 |
T label名 | 读入输入行执行命令,在使用s命令替换不成功时才会跳转到label名指定的命令进行执行 |
#分支示例
:label
command1
command2
/pattern/b label
command3
sed逐行处理读入数据并处理,所以不擅长跨行处理。
#循环示例
:loop
N <--字符串合并
$!b loop <--不是最后一行
s/\n//g