my_study_sed’s 学习笔记¶
文本处理三剑客¶
01 简介¶
SED是流编辑器。流编辑器用于在输入流(文件或管道输入)中执行基本文本转换。虽然在某种程度上类似于允许脚本编辑(如ED)的编辑器,但SED只通过一次输入而起作用,因此效率更高。但是,这是SED过滤管道中文本的能力,它特别区别于其他类型的编辑器。
02 运行sed脚本¶
2.1 预览¶
正常情况下。sed是这样使用的。
sed SCRIPT INPUTFILE...
样例1-替换
#替换hello文本为world文本
sed 's/hello/world/' input.txt > output.txt
样例2-修改文件
#替换hello文本为world文本,这个会直接修改output.txt的文件的。
sed -i 's/hello/world/' input.txt > output.txt
样例2-处理指定的行
#只打印第45行
sed -n '45p' input.txt > output.txt
2.2 命令行选项¶
全格式的sed命令格式是这样的:
sed OPTION... [SCRIPT] [INPUTFILE...]
命令行参数如下
- --version
版本信息
- --help
帮助信息
- -n
禁用自动打印
- --quiet
禁用自动打印
- --silent
禁用自动打印
- -e
添加命令
- --exoression
添加命令
- -f
命令指定在文件里面
- --file
命令指定在文件里面
- -i
替换文件
- --in-place
替换文件
- --posix
posix sed
- -b
二进制处理
- --binary
二进制处理
- --follow-symlinks
追踪符号链接文件
- -E
扩展正则
- -r
扩展正则
- --regexp-extended
扩展正则
- -s
替换
- --separate
替换
2.3 退出码¶
sed的退出码比较少,这里一一列举出来。
- 0
成功
- 1
无效命令
- 2
一个海阔这多个输入文件不能被打开
- 3
io错误
03 sed脚本预览¶
一个SED程序由一个或多个SED命令组成,它们由一个或多个E,f,-表达式,和- file选项传递,或者第一个非选项参数。
3.1 sed命令汇总¶
a 追加文本
c\ 替换行
d 删除模式空间,里面下一次循环
D 模式空间包含新行的时候,删除第一行,循环处理
e 执行命令并发送输出到输出流
F 打印当前文件名字
g 用hold空间去替换模式空间
G 追加新行到模式空间,然后追加内容到hold空间
h 用模式空间去替换hold空间
H 追加行到hold空间,然后追加模式空间到hold空间
i 插入文本
l 明确的格式打印模式空间
n 替换新行到模式空间
N 追加新行到模式空间
p 打印模式空间
P 打印模式空间+换行
q 退出,可以指定退出码
Q 退出,可以指定退出码
r 读取文件
R 当前处理周期结束,读取文件
s/regexp/replacement/[flags] 替换src模式为replacement的内容
v 版本
w 写入文件
W 写入到文件第一行
y/src/dst 转化src模式到dst内容
z 清空模式空间内容
# 一个注释
{cmd1;cmd2} 多个命令一起
= 打印当前行号
3.2 s命令汇总¶
s的命令是替换的。 基本用法是这样的。
s/regexp/replacement/flags
flags有以下几种
g 全局替换,而不是仅仅第一个
number 只匹配指定的个数
p 打印出来
w 写到文件中
I 匹配正则的时候大小写不敏感
i 匹配正则的时候大小写不敏感
3.3 经常使用的命令¶
3.3.1 #¶
#开头的作为注释
3.3.2 q [exit-code]¶
退出码,样例:
[root@centos74 ~]$ seq 3
1
2
3
[root@centos74 ~]$ seq 3 | sed 2q
1
2
[root@centos74 ~]$ echo $?
0
3.3.3 d¶
[root@centos74 ~]$ seq 3 | sed 2d
1
3
默认是打印所有行,2d删除了第二行的打印
3.3.4 p¶
[root@centos74 ~]$ seq 3 | sed -n 2p
2
-n取消了默认的打印,2p纸打印第二行
3.3.5 n¶
[root@centos74 ~]$ seq 6 | sed 'n;n;s/./x/'
1
2
x
4
5
x
这个稍有点复杂, n这个命令的意思上面提过了。 这里在说一次。 如果自动打印被禁用了。就打印模式空间,然后用新的行替换模式空间。
第一个n;就直接把第一行打印出来了。然后读取了第二行,第二个n;就把第二行打印出来了。然后读取了第三行,接下来是s替换命令,.匹配单个字符,修改为x。所以第三行的3匹配了这个正则,就被修改了。
3.4 使用较少的命令¶
3.4.1 y/source-chars/dest-chars/¶
这个命令和tr差不多的感觉。 就是对应位置替换的。
[root@centos74 ~]$ echo hello world | sed 'y/abcdefghij/0123456789/'
74llo worl3
3.4.2 a text¶
[root@centos74 ~]$ seq 3 | sed '2a hello'
1
2
hello
3
3.4.3 i text¶
[root@centos74 ~]$ seq 3 | sed '2i hello'
1
hello
2
3
3.4.4 c text¶
[root@centos74 ~]$ seq 10 | sed '2,9c hello'
1
hello
10
3.4.5 =¶
[root@centos74 ~]$ printf '%s\n' aaa bbb ccc
aaa
bbb
ccc
[root@centos74 ~]$ printf "%s\n" aaa bbb ccc | sed =
1
aaa
2
bbb
3
ccc
3.4.6 r filename¶
[root@centos74 ~]$ seq 3 | sed '2r/etc/hostname'
1
2
centos74.magedu.com
3
3.4.7 w filename¶
[root@centos74 ~]$ seq 3 |sed '2wa.txt'
1
2
3
[root@centos74 ~]$ cat a.txt
2
3.4.8 D¶
如果模式空间不包含换行符,如果D的命令发出正常的新周期的开始。否则,删除文本模式空间中的到第一个换行符,用所得的空间格局重新启动循环,没有阅读新的一行输入。
3.4.9 N¶
添加一个新行到模式空间,然后在下一行输入到模式空间。如果没有更多的输入,则SED退出而不处理任何命令。
3.4.10 p¶
打印出来的部分模式空间到第一个换行符
3.4.11 h¶
用模式空间的内容替换保持空间的内容。
3.4.12 H¶
添加一个新行的容纳空间的内容,然后将模式空间的内容,保留空间。
3.4.13 g¶
将模式空间的内容替换为保持空间的内容。
3.4.14 G¶
添加一个换行符对模式空间的内容,然后将占据空间的内容到模式空间。
3.4.15 x¶
交换保持和模式空间的内容。
3.5 多个命令一起¶
[root@centos74 ~]$ seq 6 | sed -e 1d -e 3d -e 5d
2
4
6
[root@centos74 ~]$ seq 6 | sed '1d;3d;5d'
2
4
6
04 地址选择行¶
4.1 通过数字选择行¶
主要有一下几种。
- number
直接指定某行行号即可
- $
代表末行
- first~step
指定开始行,和跳步行数
样例:
[root@centos74 ~]$ seq 3 | sed -n '2p'
2
[root@centos74 ~]$ seq 3 | sed -n '$p'
3
[root@centos74 ~]$ seq 3 | sed -n '1~2p'
1
3
4.2 通过文本去选择行¶
- /regexp/
只选择regexp这个正则表达式匹配的行,而不是默认的所有行
- %regexp%
这个是方便第一个的, 当第一个的正则中包含了大量/字符的时候。 可以考试使用这个,避免大量的转义符号。
- /regexp/I
不区分大小写匹配
- /regexp/M
使用多行模式下的, 默认sed是一行一行匹配的。跨行的是不会被匹配的。
样例:
[root@centos74 ~]$ sed -n '/csh$/p' /etc/passwd
gentoo:x:1009:1009:gentoo distribution:/home/gentoo:/bin/csh
[root@centos74 ~]$ echo "/home/alice/documents/ .hellow world" |sed -n '/^\/home\/alice\/documents\//p'
/home/alice/documents/ .hellow world
[root@centos74 ~]$ echo "/home/alice/documents/ .hellow world" |sed -n '\%^/home/alice/documents/%p'
/home/alice/documents/ .hellow world
上面的第二个和第三个功能是一样的。 明显第三个简洁了好多。
4.3 范围地址¶
- 0,/regexp/
匹配0行到指定的正则表达式直接的行
- addr1,+N
匹配地址1后的N行
- addr1,~N
匹配地址1到N的下一个倍数行
样例:
[root@centos74 ~]$ seq 10 | sed -n '1,/[0-9]/p'
1
2
[root@centos74 ~]$ seq 10 | sed -n '0,/[0-9]/p'
1
[root@centos74 ~]$ seq 10
1
2
3
4
5
6
7
8
9
10
[root@centos74 ~]$ seq 10 | sed -n '6,+2p'
6
7
8
[root@centos74 ~]$ seq 10 | sed -n '6,~4p'
6
7
8
05 正则表达式选择文本¶
使用正则表达式,那就需要详细描述下正则表达式了。 这里不描述了。 我之前写过一个awk的。也是正则表达式的。基本一样。
06 高级sed¶
6.1 sed是如何工作的¶
SED维护两个数据缓冲区:活动模式空间和辅助保持空间。两者最初都是空的。
sed通过对输入的每一行执行以下循环:第一,sed读取输入流中的一行,删除任何换行,和地方它在模式空间。然后执行命令;每个命令都可以有一个与之相关联的地址:地址是一种条件代码,如果在执行命令之前验证条件,则只执行命令。
当脚本结束,除非是在使用-n选项,模式空间的内容打印到输出流,重新加入换行如果它被删除。7再下一个周期开始为下一个输入行。
除非使用特殊命令(如“D”),否则模式空间将在两个周期之间删除。另一方面,保持空间将数据保存在循环之间(见命令“H”、“h”、“x”、“G”、“G”以便在两个缓冲区之间移动数据)。
6.2 2个模式的交换¶
- d
从模式空间删除行,直到第一个换行符,并重新启动循环。
- G
将保留空间的行追加到模式空间,并在其之前换行。
- H
将模式空间中的行追加到保存空间中,并在其之前换行。
- N
将输入文件中的行附加到模式空间。
- P
从模式空间打印行,直到第一个换行符。
样例:
[root@centos74 ~]$ seq 6 | sed -n 'N;l;D'
1\n2$
2\n3$
3\n4$
4\n5$
5\n6$
开始的时候会读取一行,存在模式空间中,内容是1n
N;这个命令会从输入文件在读取一行追加到模式空间中去,此时模式空间是1n2n
l;明确方式打印出模式空间的内容
D;将模式空间的内容删除到第一个换行符。保留了2。
继续下面的循环
07 一些样例脚本¶
7.1 合并行¶
[root@centos74 test]$ vim lines.txt
[root@centos74 test]$ cat lines.txt
hello
hel
lo
hello
[root@centos74 test]$ sed '2{N;s/\n//;}' lines.txt
hello
hello
hello
7.2 居中行¶
#!/usr/bin/sed -f
# Put 80 spaces in the buffer
1 {
x
s/^$/ /
s/^.*$/&&&&&&&&/
x
}
# delete leading and trailing spaces
y/tab/ /
s/^ *//
s/ *$//
# add a newline and 80 spaces to end of line
G
# keep first 81 chars (80 + a newline)
s/^\(.\{81\}\).*$/\1/
# \2 matches half of the spaces, which are moved to the beginning
s/^\(.*\)\n\(.*\)\2/\2\1/
7.3 增加一个行号¶
#!/usr/bin/sed -f
/[^0-9]/ d
# replace all trailing 9s by _ (any other character except digits, could
# be used)
:d
s/9\(_*\)$/_\1/
td
# incr last digit only. The first line adds a most-significant
# digit of 1 if we have to add a digit.
s/^\(_*\)$/1\1/; tn
s/8\(_*\)$/9\1/; tn
s/7\(_*\)$/8\1/; tn
s/6\(_*\)$/7\1/; tn
s/5\(_*\)$/6\1/; tn
s/4\(_*\)$/5\1/; tn
s/3\(_*\)$/4\1/; tn
s/2\(_*\)$/3\1/; tn
s/1\(_*\)$/2\1/; tn
s/0\(_*\)$/1\1/; tn
:n
y/_/0/
7.4 转化文件为小写¶
#! /bin/sh
# rename files to lower/upper case...
#
# usage:
# move-to-lower *
# move-to-upper *
# or
# move-to-lower -R .
# move-to-upper -R .
#
help()
{
cat << eof
Usage: $0 [-n] [-r] [-h] files...
-n do nothing, only see what would be done
-R recursive (use find)
-h this message
files files to remap to lower case
Examples:
$0 -n * (see if everything is ok, then...)
$0 *
$0 -R .
eof
}
apply_cmd='sh'
finder='echo "$@" | tr " " "\n"'
files_only=
while :
do
case "$1" in
-n) apply_cmd='cat' ;;
-R) finder='find "$@" -type f';;
-h) help ; exit 1 ;;
*) break ;;
esac
shift
done
if [ -z "$1" ]; then
echo Usage: $0 [-h] [-n] [-r] files...
exit 1
fi
LOWER='abcdefghijklmnopqrstuvwxyz'
UPPER='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
case `basename $0` in
*upper*) TO=$UPPER; FROM=$LOWER ;;
*) FROM=$UPPER; TO=$LOWER ;;
esac
eval $finder | sed -n '
# remove all trailing slashes
s/\/*$//
# add ./ if there is no path, only a filename
/\//! s/^/.\//
# save path+filename
h
# remove path
s/.*\///
# do conversion only on filename
y/'$FROM'/'$TO'/
# now line contains original path+file, while
# hold space contains the new filename
x
# add converted file name to line, which now contains
# path/file-name\nconverted-file-name
G
# check if converted file name is equal to original file name,
# if it is, do not print anything
/^.*\/\(.*\)\n\1/b
# escape special characters for the shell
s/["$'\\]/\\&/g
# now, transform path/fromfile\n, into
# mv path/fromfile path/tofile and print it
s/^\(.*\/\)\(.*\)\n\(.*\)$/mv "\1\2" "\1\3"/p
' | $apply_cmd
7.5 打印bash环境¶
#!/bin/sh
set | sed -n '
:x
# if no occurrence of ‘=()’ print and load next line
/=()/! { p; b; }
/ () $/! { p; b; }
# possible start of functions section
# save the line in case this is a var like FOO="() "
h
# if the next line has a brace, we quit because
# nothing comes after functions
n
/^{/ q
# print the old line
x; p
# work on the new line now
x; bx
'
7.6 反转行内的字符¶
#!/usr/bin/sed -f
/../! b
# Reverse a line. Begin embedding the line between two newlines
s/^.*$/\
&\
/
# Move first character at the end. The regexp matches until
# there are zero or one characters between the markers
tx
:x
s/\(\n.\)\(.*\)\(.\n\)/\3\2\1/
tx
# Remove the newline markers
s/\n//g