Jade Dungeon

sed

调用方式

sed [-n]    program      [file-list]
sed [-n] -f program-file [file-list]

常用参数:

  • --file program-file-f program-file
  • --in-place[=suffix]-i[suffix]:不输出到标准输出,而是修改源文件。 有[suffix]时会生成指定suffix后缀的备份文件。
  • --quiet--silent-n:除非程序中指定调用printp命令, 不然不输出内容。

也可以作为脚本调用,例如下面的程序清理每一行行尾的空格:

$ cat cleanup
#!/bin/sed -f
s/ *$//

sed程序的格式为,读取文本的每一行,对全匹配到模式的那一行执行对应的指令:

[模式[, 模式]] 指令 [指令相关的参数列表]

同一行文本可能被多个匹配到的模式处理。

例:打印出包含字符串line的那一行:

$ cat new
Line one.
The second line.
The third.
This is line four.
Five.
This is the sixth sentence.
This is line seven.
Eighth and last.

$ sed '/line/ p' new
Line one.
The second line.
The second line.
The third.
This is line four.
This is line four.
Five.
This is the sixth sentence.
This is line seven.
This is line seven.
Eighth and last.

例:参数-n设置不回显读到的每一行:

$ sed -n '/line/ p' new
The second line.
This is line four.
This is line seven.

基本概念

行号

$ sed -n '3,6 p' new
The third.
This is line four.
Five.
This is the sixth sentence.

控制结构

NOT!表示排除操作:

$ sed -n '2, 4! p' new
Line one.
Five.
This is the sixth sentence.
This is line seven.
Eighth and last.

花括号{}组合多条语句。对于匹配到的一个模式,用花括号可以组合多个操作。

Pattern区和Hold区

Pattern区是命令直接编辑的缓存区域,保存着sed刚刚从输入中读取的行; Hold区是用来暂存数据的临时缓冲区,在把数据放入Hold区之前内容为空。

在Pattern区与Hold区之间进行数据传送的命令:

  • g:Hold的内容移动并覆盖Pattern中所有的内容。
  • G:把一个换行符和Hold的内容追加到Pattern原来内容之后。
  • h:Pattern的内容移动并覆盖Hold中所有的内容。
  • H:把一个换行符和Pattern的内容追加到Hold原来内容之后。
  • x:交换Pattern和Hold中的内容。

例:通过在Hold区与Pattern区交换内容,实现文本的内容两两交换:

$ cat switchline
# 把文件中的文本每两行两两交换
h # 把刚刚读到的一行从Pattern复制到Hold
n # 把下一行读到Pattern
p # 打印Pattern的内容
g # 把Hold中的内容复制到Pattern
p # 打印Pattern的内容

$ sed -nf switchline new
The second line.
Line one.
This is line four.
The third.
This is the sixth sentence.
Five.
Eighth and last.
This is line seven.

例:G命令在复制的时候还会增加一个换行:

$ sed 'G' new
Line one.

The second line.

The third.

This is line four.

Five.

This is the sixth sentence.

This is line seven.

Eighth and last.

例子,反转所有的行:

$ cat desc
2,$ G  # 除了第一行外的每一行,都要添加到Pattern原来的内容后面
h      # Pattern覆盖到Hold
$! d   # 删除除了最后一行的所有行

$ sed -f desc new
Eighth and last.
This is line seven.
This is the sixth sentence.
Five.
This is line four.
The third.
The second line.
Line one.

$表示最后一行, $!表示最后一行外的所有行。

  Pattern Hold
第1行读到Pattern 第1行  
2,$ G不包含第一行 第1行  
h从Pattern覆盖到Hold 第1行 第1行
$! G清空Pattern   第1行
第2行读到Pattern 第2行 第1行
2,$ G追加Hold到Pattern 第2行 第1行 第1行
h从Pattern覆盖到Hold 第2行 第1行 第2行 第1行
$! G清空Pattern   第2行 第1行
第3行读到Pattern 第3行 第2行 第1行
2,$ G追加Hold到Pattern 第3行 第2行 第1行 第2行 第1行
h从Pattern覆盖到Hold 第3行 第2行 第1行 第3行 第2行 第1行
$! G清空Pattern   第3行 第2行 第1行
....... ....... .......
第8行读到Pattern 第8行 第7行 ... 第1行
2,$ G追加Hold到Pattern 第8行 第7行 ... 第1行 第7行 ... 第1行
h从Pattern覆盖到Hold 第8行 第7行 ... 第1行 第8行 第7行 ... 第1行
$! G最后一行不执行 第8行 第7行 ... 第1行 第8行 第7行 ... 第1行

常用指令

打印指令p

$ sed '/line/ p' new
Line one.
The second line.
The second line.
The third.
This is line four.
This is line four.
Five.
This is the sixth sentence.
This is line seven.
This is line seven.
Eighth and last.

退出指令q

读到第五行就退出程序

$ sed '5 q' new
Line one.
The second line.
The third.
This is line four.
Five.

后面添加文本指令a

在第二行后添加新行,内容为【AFTER】:

$ sed '2 a AFTER.' new
Line one.
The second line.
AFTER.
The third.
This is line four.
Five.
This is the sixth sentence.
This is line seven.
Eighth and last.

前面添加文本指令i

模式【This】前添加新行,内容为【BEFORE】:

$ sed '/This/ i BEFORE.' new
Line one.
The second line.
The third.
BEFORE.
This is line four.
Five.
BEFORE.
This is the sixth sentence.
BEFORE.
This is line seven.
Eighth and last.

文本修改指令change

$ sed '/second/ c 2nd' new
Line one.
2nd
The third.
This is line four.
Five.
This is the sixth sentence.
This is line seven.
Eighth and last.

如果用行号指定范围,替换的不是每一行,而是所有行作为一个整体替换:

$ sed '2,4 c\
  lin1\
  lin2\
  lin3' new

Line one.
lin1
lin2
lin3
Five.
This is the sixth sentence.
This is line seven.
Eighth and last.

正则表达式替换substitute

正则表达式标志p表示替换后的每一行都要打印出来:

$ sed -n 's/line/sentence/p' new
The second sentence.
This is sentence four.
This is sentence seven.

正则表达式标志w指定输出到文件temp

$ sed -n 's/line/sentence/w temp' new

$ cat temp
The second sentence.
This is sentence four.
This is sentence seven.

正则表达式标志g表示同一行中出现的多个匹配都会被替换:

$ sed -n 's/line/sentence/g' new

例子:修改多个文件内容的脚本:

$ cat sub
#!/bin/bash
for file
do
	echo $file
	mv $file $$.subhld
	sed 's/REPORT/report/g
			 s/FILE/file/g
			 s/PROCESS/process/g' $$.subhld > $file
done
rm $$.subhld

$ bash ./sub file1 file2 file3
file1
file2
file3

$ cat file*
report
file
process
report
file
process
report
file
process

正则表达式替换时,符号'&'代表匹配到的内容:

$ cat example.properties
username=
password=

$ sed -n 's/username=/&password/p
  s/password=/&p@ssw0rd/p' example.properties

username=password
password=p@ssw0rd

写入到文件指令write

$ sed -n '2,4 w temp2' new

$ cat temp2
The second line.
The third.
This is line four.

跳过一行的指令next

$ sed -n '/the/ n
  p' new

Line one.
The second line.
The third.
This is line four.
Five.
This is line seven.
Eighth and last.

例子

替换文件中的引号

Unicode 序列u2018u2019产生, 而代码u201cu201d产生字符:

#!/bin/sh
# GNU All-Permissive License

SDQUO=$(echo -ne '\u2018\u2019')
RDQUO=$(echo -ne '\u201C\u201D')
$SED -i -e "s/[$SDQUO]/\'/g" -e "s/[$RDQUO]/\"/g" "${1}"

调用的例子;

$ cat test.txt
‘Single quote’
“Double quote”
$ bash ./fixquotes.sh test.txt
$ cat test.txt
'Single quote'
"Double quote"

修改远程文件

#!/usr/bin/expect  -f
if { $argc != 6 } {
 send_user "usage: expect change-config-sample.exp hostname portnum uname password filename value1 value2 \n"
 exit
}
#define var
set timeout 30
set hostname [lindex $argv 0]
set portnum  [lindex $argv 1]
set uname    [lindex $argv 2]
set password [lindex $argv 3]
set filename [lindex $argv 4]
set value1   [lindex $argv 5]
set value2   [lindex $argv 6]
set cmd01 " sed -i 's/key1=.*$/key1=${value1}/' ${filename} \n"
set cmd02 " sed -i 's/key2=.*$/key1=${value2}/' ${filename} "
set cmd99 " echo -e \" ${cmd01} ${cmd02} \" | bash "
set cmdstr " ${cmd99} && sync && reboot "
spawn ssh ${uname}@${hostname} -p ${portnum} ${cmdstr}
expect {
        "yes/no"     { send "yes\r"; exp_continue }
        "*password:" { exp_send "${password}\r" }
        timeout      { puts "expect connect timeout,pls contact xp"; return; }
}
expect eof
exit

调用方式:

expect change-clientcode-on-linux.exp "127.0.0.1" 22 "root" "password" \
         "/etc/myapp/cfg.properties" "value1" "value2" ;               \
        sleep 1;  read -p "Press enter to continue";