my_study_sed’s 学习笔记

文本处理三剑客

grep学习

sed学习

awk学习

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$
  1. 开始的时候会读取一行,存在模式空间中,内容是1n

  2. N;这个命令会从输入文件在读取一行追加到模式空间中去,此时模式空间是1n2n

  3. l;明确方式打印出模式空间的内容

  4. D;将模式空间的内容删除到第一个换行符。保留了2。

  5. 继续下面的循环

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

7.7 更多样例

更多样例