专业做淘宝网站,互动营销网站建设,代表性设计制作作品图片,wordpress改变链接地址环境
Ubuntu 22.04
概述
sed 是“stream editor”的意思#xff0c;用来处理文本#xff0c;比如做文本查找、替换。
处理单行文本
用法1
语法#xff1a; sed s/aaaa/bbbb/
aaaa #xff1a;用正则表达式来匹配字符串#xff0c;并用圆括号括起来需要获取的内容用来处理文本比如做文本查找、替换。
处理单行文本
用法1
语法 sed s/aaaa/bbbb/
aaaa 用正则表达式来匹配字符串并用圆括号括起来需要获取的内容如 xx\(yy\)zzbbbb 获取第几个圆括号里的内容如 \1
比如有一个由3个冒号“ : ”所分隔的字符串比如 a:b:c:d 。想要获取第一个冒号之前的内容。
从正则表达式角度该字符串可以看做
.*:.*:.*:.*
分别用圆括号 () 把两个冒号之间的内容括起来即
(.*):(.*):(.*):(.*)
注意圆括号需要转义所以是
\(.*\):\(.*\):\(.*\):\(.*\)
然后指定获取第几部分比如第一部分就是 \1 注意需要转义。
完整的例子如下
➜ ~ echo a:b:c:d | sed s/\(.*\):\(.*\):\(.*\):\(.*\)/\1/
a➜ ~ echo a:b:c:d | sed s/\(.*\):\(.*\):\(.*\):\(.*\)/\2/
b➜ ~ echo a:b:c:d | sed s/\(.*\):\(.*\):\(.*\):\(.*\)/\3/
c➜ ~ echo a:b:c:d | sed s/\(.*\):\(.*\):\(.*\):\(.*\)/\4/
d贪婪匹配
如果我们把 a:b:c:d 用 .*:.* 来通配即分成两部分那么sed会怎么来划分呢
答案第一部分是 a:b:c 第二部分是 d 。
➜ ~ echo a:b:c:d | sed s/\(.*\):\(.*\)/\1/
a:b:c➜ ~ echo a:b:c:d | sed s/\(.*\):\(.*\)/\2/
d这是因为在正则表达式里 .* 是“贪婪式”的会尽可能多的向右匹配。
所以如果我们只想获取最后一个冒号右边的内容并不需要把整个字符串按每个冒号划分为多个部分而只需用 .*:.* 来通配参见上面的例子。
sed貌似无法指定非贪婪式匹配。
如果我们想要获取第一个冒号前面的内容可以像前面所介绍的按照每个冒号分隔为多个部分然后取第一部分。但是这种做法其实不太方便比如如果字符串发生变化多了一个冒号那么正则表达式也得随之变化否则就会取错内容。
一个变通的方法是从左到右把所有非冒号的字符 [^:]* 作为第一部分 ^ 表示“非”后面就是 :.* 即冒号以及随后的所有内容。
➜ ~ echo a:b:c:d | sed s/\([^:]*\):.*/\1/
a用法2
sed还有一种用法获取正则表达式匹配之外的内容。
语法 sed s/aaaa//
aaaa 是一个正则表达式
例如
➜ ~ echo abcdefg | sed s/cde//
abfg正则表达式匹配了 cde 则获取的是前面和后面没有匹配上的内容即 ab 和 fg 。
按此方法对于 a:b:c:d 要获取最后一个冒号右边的内容可以简化如下
➜ ~ echo a:b:c:d | sed s/\(.*\)://
d若正则没有匹配成功则获取整个字符串
注意sed的这两种用法有一个共同的行为特点如果正则表达式没有匹配成功则会获取整个字符串。
比如
➜ ~ echo abcd | sed s/ab\(c\)d/\1/
c这是正确的匹配。如果正则没有匹配上
➜ ~ echo abcd | sed s/zzzab\(c\)d/\1/
abcd则获取了整个字符串。
第二种用法也同理
➜ ~ echo abcd | sed s/abc//
d这是正确的匹配。如果正则没有匹配上
➜ ~ echo abcd | sed s/zzzabc//
abcd则获取了整个字符串。
处理多行文本比如文件
查找内容
假设在 ~/.zshrc 里包含了 JAVA_HOME 设置
➜ ~ grep JAVA_HOME ~/.zshrc
export JAVA_HOME/home/ding/Downloads/jdk-17.0.3.1
export PATH${JAVA_HOME}/bin:$PATH现在需要通过sed获取 JAVA_HOME 的设置即 /home/ding/Downloads/jdk-17.0.3.1 。
一种方法是先通过grep命令找到那一行假设只有一行满足条件然后就只需处理单行文本
➜ ~ cat ~/.zshrc | grep JAVA_HOME | sed s/.*JAVA_HOME\(.*\)/\1/
/home/ding/Downloads/jdk-17.0.3.1修改文件内容
比如要把 JAVA_HOME 设置为 myPath 。
语法 sed -i /aaaa/s/bbbb/cccc/
aaaa 正则表达式用来匹配行s 表示替换bbbb 正则表达式表示旧内容cccc 新内容
其中 -i 表示修改文件否则只会把结果输出。
注可以在最后加一个 g 表示对本行做全局替换否则只替换第一处。本例中只有一处所以无需加 g 。
sed -i /JAVA_HOME/s/.*/myPath/ ~/.zshrc本例中
aaaa 为 JAVA_HOME 即查找符合条件的行bbbb 为 .* 即等号以及后面所有内容cccc 为目标字符串
运行后文件内容被修改为
......
export JAVA_HOMEmyPath
......注意这里的 bbbb 无需匹配全部内容而只是需被替换的内容而前面介绍的从字符串获取文本时正则表达式必须要匹配全部内容。
如果目标字符串需要包含 bbbb 的内容则可用 来代表。
比如要把 export JAVA_HOMExxxxx 改为 export JAVA_HOMExxxxx/jdk
sed -i /JAVA_HOME/s/.*/\/jdk/ ~/.zshrc运行后文件内容被修改为
......
export JAVA_HOME/home/ding/Downloads/jdk-17.0.3.1/jdk
......获取一部分内容
语法 sed -n /xxxx/,/yyyy/p
表示输出从 xxxx 到 yyyy 的行。其中 yyyy 可以为空表示输出到最末。
比如创建文件 test1.txt 如下
abcdefg
hijklmn
opq
rst
uvw
xyz则
➜ ~ sed -n /jk/,/st/p test1.txt
hijklmn
opq
rst➜ ~ sed -n /cde/,/uv/p test1.txt
abcdefg
hijklmn
opq
rst
uvw➜ ~ sed -n /w/,//p test1.txt
uvw
xyz在一部分内容里做替换
语法 sed -i /xxxx/,/yyyy/s/aaaa/bbbb/
xxxx 和 yyyy 开始行和结束行也就是指定的部分内容aaaa 旧内容bbbb 新内容
也就是说在 xxxx 和 yyyy 所指定的范围内把 aaaa 替换为 bbbb 。
比如 test1.txt 内容如下
rst
abcdefg
hijklmn
opq
rst
uvw
xyz要把所有的 rst 里的 rs 替换为 123
sed -i /.*/s/rs/123/ test1.txt而如果只把下方的 rst 里的 rs 替换为 123
sed -i /pq/,/u/s/rs/123/ test1.txt其中 pq 和 u 划定了处理内容的范围。
分隔符
如果字符串里本身含有 / 则匹配时需要用 \ 来转义。
比如要想获取字符串 /etc/ansible/hosts 里的 ansible
➜ ~ echo /etc/ansible/hosts | sed s/\/etc\/\(.*\)\/hosts/\1/
ansible这里的正则表达式为 \/etc\/\(.*\)\/hosts 较为复杂。
为了方便我们可以换一个分隔符比如换成 |
➜ ~ echo /etc/ansible/hosts | sed s|/etc/\(.*\)/hosts|\1|
ansible这里的正则表达式为 /etc/\(.*\)/hosts 相对简单一些。
考一考
1
求输出结果
echo a:b:c:d | sed s/\(.*\):\(.*\):\(.*\)/\1/答贪婪式匹配
➜ ~ echo a:b:c:d | sed s/\(.*\):\(.*\):\(.*\)/\1/
a:b2
求输出结果
echo a:b:c | sed s/\(.*\):\(.*\):\(.*\):\(.*\)/\1/答没有匹配成功则获取整个字符串
➜ ~ echo a:b:c | sed s/\(.*\):\(.*\):\(.*\):\(.*\)/\1/
a:b:c3
已知字符串 {key1:value1,key2:value2,......} 从中获取 value2 的值 key2 是已知字符串且 value2 中不含引号 。
答
➜ ~ echo {key1:value1,key2:value2,key3:value3} | sed s/.*key2:\([^]*\).*/\1/
value2