sed 命令
sed 简述
sed [选项] [脚本] [输入文件]
|
在命令行上运行 script (指令),只有一组指令时可不需要本选项。 |
|
支持延伸正规表示法(缺省是基本正规表示法)。 |
|
运行 script-file (指令档), sed 的指令是在指令档内。 |
|
直接修改文件内容。 |
|
separate lines by NUL characters,将换行以 NUL 字符取代,取代换行时需本选项。 |
|
不自动打印 (模式空间)。 |
sed 's/pattern/replacement/flags' inputFileName > outputFileName
替代命令 (在脚本中第一个字母 s
) 将 inputFileName 的内容与提供的样板 (pattern) 匹配;当匹配成功则将样板替换为「替换文本 (replacement)」。
/ (斜线) 是传统的定界字符 (delimiter),但;其实在 pattern 和 replacement 中都未出现的其他字符都可以当作定界字符。pattern 和 replacement 常称为 正规表示法。
sed 命令中可以用单引号 ('
) 和双引号 ("
),一般习惯采用单引号,但是如果要使用 shell 变量就需要使用双引号。
flags 指定为 g
时,表示 全局更改。
将下列的 is 改成 is a:
echo 'this is book, that is bicycle' | sed -e 's/ is / is a /' (1)
this is a book, that is bicycle
以全局 (g) 更改:
echo 'this is book, that is bicycle' | sed 's/ is / is a /g'
this is a book, that is a bicycle
1 | 只有一组指令,可不需要 -e 选项。 |
正规表示法
正则表达式 (英语: Regular Expression,简写为 regex、regexp 或 RE),又称正规表示法。正规表示法分为基本正规表示法(Basic Regular Expression,BRE)和延伸正规表示法(Extended Regular Expression,ERE)。
sed 采用 ERE 的选项为 -E
或 -r
。
- 正规表示法的特殊字符
-
. * ^ $ / \ [ ]
上述特殊字符作为文本字符时,需要在特殊字符前加入「转义字符 (Escape Character)
\
(反斜线)」 来转译它。:sed 的定界字符如果是
/
(斜线),那么文本字符的「斜线」要转译成\/
,如果不是则不可转译。
改变定界字符的目的就是为了不要转译「斜线」,如果又转译那么在 BRE 不会出错,ERE 会出错 (参阅: 测试转译斜线)。? + | ( ) { }
上述特殊字符作为文本字符时,在 BRE 不需转译,但在 ERE 需要转译。参阅:测试「特殊字符」转译。
-
作为 BRE 文本字符时,不需要 (也不可) 转译,但作为匹配字符时需转译。
-
作为 ERE 文本字符时,需转译,但作为匹配字符时,不需要 (也不可) 转译。
sed 的脚本如果是使用单引号 (
'
),而文本中有单引号时,那么 bash 要转译文本字符为\'
,反之如果是双引号 ("
) 则要转译\"
。- bash 在双引号时,除了转译
"
也要注意字符\
$
(作为文本字符,并非引用变量) 也需要转义字符 -
echo "\" \\ \$"
运行结果:" \ $
当文本内容含「特殊字符」时,需注意 bash 是否正确,如将
$'pattern'
替换为$'replacement'
echo "\$'pattern'" | sed "s/\$'pattern'/\$'replacement'/"
'replacement'
结果无误,但实际上是错的
显示整段命令:echo "\$'pattern'" \| sed "s/\$'pattern'/\$'replacement'/"
'pattern' | sed s/$'pattern'/$'replacement'/ (1)
1 没有转译 $
,但刚好 BRE 可在不需要转译的情况下运行,但如果改为 ERE 则会出错。在 测试「特殊字符」转译 如下specChar='$' 3.14159 (BRE 转译) 3.14159 (BRE 不转译) 3.14159 (ERE 转译) 3$14 (ERE 不转译)
在写 bash 时,
echo
会记得转译$
,但;当重点放在 sed 转译特殊字符时,往往会忽略 (忘记) 了 bash 在双引号 ("
) 时也需要转译\
$
。先显示整段命令,确认 bash 无误:echo "\$'pattern'" \| sed "s/\\\$'pattern'/\\\$'replacement'/"
运行结果:'pattern' | sed s/\$'pattern'/\$'replacement'/
以 BRE ERE 运行命令:echo "\$'pattern'" | sed "s/\\\$'pattern'/\\\$'replacement'/" &&\ echo "\$'pattern'" | sed -E "s/\\\$'pattern'/\\\$'replacement'/"
-
- 匹配字符
-
- 字符
.
-
匹配一个任意字符。
- 字符
^
-
匹配起始位置。
- 字符
$
-
匹配结束位置。
- 字符
*
-
表示前置字符有任意个 (包含 0 个)。
如a*b
表示b
的前面有 0 个或多个a
,匹配b
、aaab
。而.*
则表示全部文本。 - 字符
?
-
表示前置字符有 0 个或 1 个。BRE 需要使用
\?
。
如a\?b
表示b
的前面有 0 个或 1 个a
,匹配b
、ab
,不匹配aab
。
?
作为 BRE 匹配字符时,需要前置转义字符 (\
),但作为文本字符时不可加入转义字符。
?
作为 ERE 匹配字符时,不需要 (也不可有) 转义字符,但作为文本字符时需要前置转义字符。 - 字符
+
-
表示前置字符有 1 个或多个,BRE 需要使用
\+
。
如a\+b
表示b
的前面有 1 个或多个a
,匹配ab
、aaab
,不匹配b
。 - 字符
|
-
表示指明两项之间的一个选择,
cat|dog
表示可以匹配cat
或者dog
,BRE 需使用\|
。将monkey
moose
隐藏:echo 'meerkat monkey moose mule' | sed 's/monkey\|moose/hide/g' &&\ echo 'meerkat monkey moose mule' | sed -E 's/monkey|moose/hide/g'
meerkat hide hide mule meerkat hide hide mule
- 字符
&
-
合乎匹配的文本。
前置任意字符的ats
,加上中括号echo 'Bats and cats.' | sed 's/.ats/\[&\]/g'
[Bats] and [cats].
- 字符
[]
-
中括号中可以包含表示字符集 (character sets) 的表达式,使用方法大概有如下几种: [0-9]
表示 0-9 字符中的一个。
[0-9.]
表示数字和小数点。
[a-z]
表示 a-z 字符中的一个。
[A-Z]
表示大写字母。
[a-zA-Z0-9]
大小写字母和数字。
[abc]
表示字符 a 或者字符 b 或者字符 c。
[^0-9]
表示非数字的字符,
^
表示取反意思,只能放在中括号的开始处才有意义。[-az]
表示字符 - 或者字符 a 或者字符 z,注意与
[a-z]
的区别,因为 - 字符没有放在 a 和 z 之间。将动物前置 mo mu 隐藏 (找出前置 m 后接 [ou] 再接任意个 [a-z]*):echo 'meerkat monkey moose mule' | sed 's/m[ou][a-z]*/hide /g' &&\ echo 'meerkat monkey moose mule' | sed -E 's/m[ou][a-z]*/hide /g'
meerkat hide hide hide meerkat hide hide hide
将参数改名,匹配开始字符,任意个不等于「结束字符」的字符,再匹配结束字符echo 'show(abc)' | sed 's/([^)]*)/(def)/' &&\ echo 'show(abc)' | sed -E 's/\([^\)]*\)/\(def\)/'
show(def) show(def)
取代任意文本,要指定可取代那些字符;最好的方式是字符条件不等于 pattern 的结束字符 ( [^pattern]
)。- 字符
[[: :]]
-
sed 除了正规的字符集外还支持命名字符类 (named character classes),命名类必须在两个中括号内使用。
命名字符类的等效字符集如下: [[:alnum:]]
[A-Za-z0-9]
大小写字母和数字。
[[:alpha:]]
[A-Za-z]
英文本母。
[[:blank:]]
[ \x09]
空格或制表字符 (Tab 键)。
[[:cntrl:]]
[\x00-\x19\x7F]
控制字符。
[[:digit:]]
[0-9]
数字。
[[:graph:]]
[!-~]
空白字符之外的 (可见) 字符。
[[:lower:]]
[a-z]
小写字母。
[[:print:]]
[ -~]
可打印的字符(类似
[[:graph:]]
,但包括空白字符)。[[:punct:]]
[!-/:-@[-`{-~]
标点符号。
[[:space:]]
[ \f\n\r\t\v]
所有空白字符。
[[:upper:]]
[A-Z]
大写字母。
[[:xdigit:]]
[0-9a-fA-F]
16 进制字符。
[^[:digit:]]
[^0-9]
表示非数字的字符,
^
表示取反意思,只能放在第二个中括号的开始处才有意义。
- 字符
- 字符
\
-
\a
Alert (警报) beep,哔声,等价于
\x07
和\cG
。\b
匹配「单词」边界。注:单词为字母、数字或底线 (
_
)。\B
匹配非单词边界。(
\B
的匹配条件跟\b
相反。)\d
匹配数字 (含中文全型数字),等价于
[0-9]
。\D
匹配非数字,等价于
[^0-9]
。\f
匹配换页字符,等价于
\x0c
和\cL
。\n
匹配换行字符,等价于
\x0a
和\cJ
。\r
匹配确认键,等价于
\x0d
和\cM
。\s
匹配空白字符;包括空格 (含中文全型空格)、换页字符、制表字符等等,等价于
[ \f\n\r\t\v]
。\S
匹配非空白字符,等价于
[^ \f\n\r\t\v]
。\t
匹配一个制表字符,等价于
\x09
和\cI
。\v
匹配一个垂直制表字符,等价于
\x0b
和\cK
。\w
匹配「单词」字符,等价于
[A-Za-z0-9_]
。\W
匹配非单词的字符,等价于
[^A-Za-z0-9_]
。\cx
匹配控制字符 x,例如,
\cM
为一个 Ctrl+M 或确认键。\xnn
16 进制数值 nn,如
\x0a
为换行。\dnum
10 进制数值 num,如
\d10
为换行。\u
在 sed 中是将字符转成大写,并非作为 Unicode 编码,参阅:特殊串行。
匹配「单词」边界 (
\b
)。将边界以中括号标示echo 'at attach cattle cat' | sed -e 's/\b/\[&\]/g' &&\ echo 'at attach cattle cat' | sed -E 's/\b/\[&\]/g'
[]at[] []attach[] []cattle[] []cat[] []at[] []attach[] []cattle[] []cat[]
边界是一个抽象的匹配,在边界时条件为吻合。
先匹配文本at
再匹配边界\b
echo 'at attach cattle cat' | sed 's/at\b/&\[\]/g' &&\ echo 'at attach cattle cat' | sed -E 's/at\b/&\[\]/g'
at[] attach cattle cat[] at[] attach cattle cat[]
匹配文本
at
条件之后要为边界才为吻合。先匹配边界\b
再匹配文本at
echo 'at attach cattle cat' | sed 's/\bat/\[\]&/g' &&\ echo 'at attach cattle cat' | sed -E 's/\bat/\[\]&/g'
[]at []attach cattle cat []at []attach cattle cat
边界条件匹配后,再匹配文本
at
才为吻合。
无边界文本,匹配「边界」。echo '12345678' | sed 's/\b/\[&\]/g' []12345678[]
在文本的前后边界时条件为吻合。
千位分隔符号,在一段连续的数字中,由右方开始算起,每隔三位数加进一个逗号。
在数字右方 (边界),开始算起接连三个数字,加入一个逗号。
用 sed 的说明:在匹配三个数字 ([0-9]{3}
) 后要接「边界」(\b
)。匹配三个数字 ([0-9]{3}
) 后要接「边界」(\b
)echo '12345678' | sed 's/[0-9]\{3\}\b/\[&\]/' 12345[678]
上述虽然正确,但如果数字为123
也会合乎条件,匹配式有错。echo '123' | sed 's/[0-9]\{3\}\b/\[&\]/' [123]
条件需再加上,开始的「非边界」。
在「非边界」之后,接三个数字,在三个数字之后要接「边界」。条件是
-
非边界
/B
-
三个连续数字 [0-9]{3}
-
边界
/b
echo '12345678' | sed 's/\B[0-9]\{3\}\b/\[&\]/' &&\ echo '12345678' | sed -E 's/\B[0-9]{3}\b/\[&\]/' &&\ echo '123' | sed 's/\B[0-9]\{3\}\b/\[&\]/' &&\ echo '123' | sed -E 's/\B[0-9]{3}\b/\[&\]/'
12345[678] 12345[678] 123 123
注:接下来要「分支」的动作,才能完成
12,345,678
在 \B
\b
之间的匹配,可当做是由后面 (右边) 开始,向前面 (左边) 匹配。
「单词」有那些?
测试「单词」边界:echo "abc +-*/=. 123 _" | sed 's/\b/X/g'
单词边界结果:XabcX +-*/=. X123X X_X
单词为字母、数字或底线 (
_
)。
匹配换行
\n
,sed 需要使用-z
选预。将换行替换为,
seq 3 | sed -z 's/\n/,/g'
如何处理 Unicode 编码?
如Σ
的 Unicode 编码为\u03a3
。echo -n Σ | iconv -t JAVA \u03a3
-
直接采用 Unicode 字符。
-
由
echo -n Σ | od -tx1
或echo -n Σ | hexdump -C
转换成 16 进位字符 (ce
、a3
)。 -
以 bash echo 来转换如
'$(echo -ne '\u03a3')'
。 -
以 bash 转换如
'$'\u03a3''
echo Σ | sed 's/Σ/X/' &&\ echo Σ | sed 's/\xce\xa3/X/' &&\ echo Σ | sed 's/'$(echo -ne '\u03a3')'/X/' &&\ echo Σ | sed 's/'$'\u03a3''/X/'
X X X X
-
- 字符
(
pattern)
-
表示分组「Group」,在
(
)
之间为匹配样板 (pattern),BRE 需使用\(
及\)
。可以通过回调参数\1
、\2
、\3
至最大值为\9
来表示分组匹配内容。只打印匹配分组样板monkey
:echo 'mule monkey moose' | sed 's/.*\(monkey\).*/\1/' &&\ echo 'mule monkey moose' | sed -E 's/.*(monkey).*/\1/'
monkey monkey
将参数 b a 互换 a1 b2echo 'add(b, a)' | sed 's/add(\([^,]\+\), *\([^)]\+\)/add(\21, \12/' &&\ echo 'add(b, a)' | sed -E 's/add\(([^,]+), *([^\)]+)/add(\21, \12/'
add(a1, b2) add(a1, b2)
回调参数最大为
\9
,\21
是指回调第2
组再加上数字1
。 - 字符
{
n,m}
-
{n}
匹配 n 次。如
o{2}
不能匹配fox
中的一个o
,但是能匹配google
中的两个o
。{n,}
至少匹配 n 次。如
o{2,}
不能匹配fox
中的一个o
,但能匹配goooogle
中的所有o
。
o{1,}
等价于o+
,o{0,}
则等价于o*
。{n,m}
至少匹配 n 次且最多匹配 m 次。如
o{1,3}
,将匹配goooogle
前三个o
。
o{0,1}
等价于o?
BRE 需使用
\{n,m\}
。匹配一个或多个小写 o,替换为一个大写 O:echo 'meerkat monkey moose mule' | sed 's/o\{1,\}/O/g' &&\ echo 'meerkat monkey moose mule' | sed 's/o\+/O/g' &&\ echo 'meerkat monkey moose mule' | sed -E 's/o{1,}/O/g' &&\ echo 'meerkat monkey moose mule' | sed -E 's/o+/O/g'
meerkat mOnkey mOse mule meerkat mOnkey mOse mule meerkat mOnkey mOse mule meerkat mOnkey mOse mule
- 字符
- 字符
(
及)
在 BRE (基本正规表示法) 及 ERE (延伸正规表示法) 的差异 -
文本字符
(
及)
在 BRE 不是特殊字符不需要转译。如替换 (Hello) 取代成 hi:echo '(Hello)' | sed 's/(Hello)/hi/' hi
如果转译成\(
及\)
,则变成 BRE 分组「Group」。echo '(Hello)' | sed 's/\(Hello\)/hi/' (hi)
但为什么会有这样的结果?
「分组」取得分组样板Hello
,将其分组样板替换为hi
,
原始文本 (Hello) 为 (「分组样板」),将其「分组样板」替换 hi 其结果为 (hi)。文本字符(
及)
在 ERE 需要转译:echo '(Hello)' | sed -E 's/\(Hello\)/hi/' hi
不转译(
及)
,则为 ERE 分组「Group」。echo '(Hello)' | sed -E 's/(Hello)/hi/' (hi)
sed 指令 (script)
sed 进程由一个或多个 sed 指令 (script) 组成,由多个 -e
选项传入或者运行 -f
指定的指令档。
[选择行号] 命令 [选项]
- 多指令语法
-
指令的范例是将
2
替换为2B
、3
替换为3C
。多指令使用-e
选项seq 5 | sed -e 's/2/2B/' -e 's/3/3C/' (1)
1 -e
之前要空格,-e
之后可不需要空格。使用分号 (;
)seq 5 | sed 's/2/2C/; s/3/3C/' (1)
1 分号之后可不需要空格。 使用换行seq 5 | sed 's/2/2B/ s/3/3C/ '
使用文件 (-f
选项):cat > script.sed << EOF s/2/2B/ s/3/3C/ EOF seq 5 | sed -f script.sed
- sed 管道
-
当读取跟输出的文件相同时,如果以下列方式运行:
sed command $File | sed command > $File (1) cat $File | sed command | sed command > $File (1)
1 将得到一个空的 $File,原因是在创建管道时,输出管道 > $File
会先运行将创建一个空的 $File。可使用临时文件来解决此问题:sed command $File | sed command > tmpFile mv tmpFile $File cat $File | sed command | sed command > tmpFile mv tmpFile $File
选择行号 (Addressing)
|
指定「行号 (number 数字)」仅匹配该「行号」。 |
|
由 first 行号 (数字) 开始,间隔 ( |
|
匹配最后一行 |
|
匹配正则表达式 regexp 的「行」。 |
|
同样是匹配正则表达式 regexp,但允许使用与 |
|
regexp 不区分大小写 |
|
多行模式空间采用修饰字符 |
|
上述可以采用逗号 ( |
|
行号 0 可以用在地址规范中,例如 |
|
由 addr1 行开始,再接着往下数 N 行,也就是 addr1 至 addr1+N 行。 |
|
由 addr1 行开始至 ( |
- number
-
第 2 行运行打印
seq 3 | sed -n '2 p' (1) 2
1 sed 使用 -n
选项 (不自动输出),由p
(打印) 命令来输出,另外2 p
中间可不需要空格字符。- first~step
-
从第 2 行开始,每隔 (
~
) 3 行,运行打印seq 9 | sed -n '2~3 p' 2 5 8
- regexp
-
找出
B
行,运行打印printf '%s\n' 1A 2B 3C | sed -n '/B/ p' &&\ printf '%s\n' 1A 2B 3C | sed -n '/b/I p' 2B 2B
找出不存在的F
,运行打印printf '%s\n' 1A 2B 3C | sed -n '/F/ p'
没有输出。
- \%regexp%
-
采用定界字符
%
及#
,不需转译/
。找出含字符/
的行,运行打印。printf '%s\n' 1A 2/ 3C | sed -n '\%/% p' &&\ printf '%s\n' 1A 2/ 3C | sed -n '\#/# p' &&\ printf '%s\n' 1A 2/ 3C | sed -n '/\// p' 2/ 2/ 2/
$
需要转译成\$
,表达式\%regexp%
是允许使用与/
不同的定界字符,并不是不需要「转译」。找出$
行,运行打印printf '%s\n' 1A 2$ 3C | sed -n '\%\$% p' &&\ printf '%s\n' 1A 2$ 3C | sed -n '/\$/ p' 2$ 2$
- /regexp/M
-
模式空间只有一行时,能匹配
^
、$
。打印开头 (^
) 之后为2
或者2
在结束 ($
) 之前的行。seq 3 | sed -n '/^2/ l' &&\ seq 3 | sed -n '/2$/ l'
2$ 2$
但在多行时,无法匹配
2
,因为2
不在整个数据的开头之后或结束之前。下列将无输出seq 3 | sed -n 'N;N; /^2/ p' &&\ seq 3 | sed -n 'N;N; /2$/ p'
采用修饰字符
M
,则能匹配每行的^
、$
。匹配第 2 行的^
、$
:seq 3 | sed -n 'N;N; /^2/M l' &&\ seq 3 | sed -n 'N;N; /2$/M l'
1\n2\n3$ 1\n2\n3$
- n,m
-
第 2 行 至(
,
) 第 4 行运行打印seq 100 | sed -n '2,4 p' 2 3 4
第 4 行 至(,
) 第 2 行,只有第 4 行运行打印 (行号 4 至行号 2,只有行号 4 合乎)printf '%s\n' 1A 2B 3C 4D 5E | sed -n '4,2 p' 4D
第 D 行 至(,
) 第 2 行运行打印,只有 D 行运行打印 (D 行号为 4 至行号 2,只有行号 4 合乎)printf '%s\n' 1A 2B 3C 4D 5E | sed -n '/D/,2 p' 4D
第 2 行 至(,
)D
行运行打印printf '%s\n' 1A 2B 3C 4D 5E | sed -n '2,/D/ p' 2B 3C 4D
第 4 行 至(,
)B
行运行打印,regexp 没有匹配B
,打印 第 4 行至最后一行 (regexp 没有匹配,则范围由第一个位置至最后一行)。printf '%s\n' 1A 2B 3C 4D 5E | sed -n '4,/B/ p' 4D 5E
第 1 行 至(,
)A
行运行打印,regexp 没有匹配A
(匹配起始位置是第一个位置之后),打印 第 1 行至最后一行。printf '%s\n' 1A 2B 3C 4D 5E | sed -n '1,/A/ p' 1A 2B 3C 4D 5E
- 0,regexp
-
第 0 行 至(
,
)A
行运行打印printf '%s\n' 1A 2B 3C | sed -n '0,/A/ p' 1A
第 0 行 至(,
) 不存在F
行运行打印,regexp 没有匹配,打印全部。printf '%s\n' 1A 2B 3C | sed -n '0,/F/ p' 1A 2B 3C
- addr1,+N
-
从第 2 行开始,再接着往下数 (
+
) 3 行,也就是 2 至 5 行运行打印seq 100 | sed -n '2,+3 p' 2 3 4 5
- addr1,~N
-
由第 4 行开始 至(
,
) 下一个倍数 (~
) 3 的行号,也是 4 至 6 运行打印。seq 100 | sed -n '4,~3 p' 4 5 6
选择行号「分组」表达式
选择行号的 BRE 分组功能 \(
regexp\)
,ERE 需使用 (
regexp)
,所选取的内容为两组相同;分组匹配的内容跟回调内容 (\1
) 相同,如匹配内容为 9
回调内容亦为 9
则匹配 99
的行。
seq 87 110 | sed -En '/(.)\1/p'
88 99 100 110
\19
,回调参数最大为 \9
,\19
是指回调第 1
组再加上字符 9
。seq 100 400 | sed -En '/(.)\19/p'
119 229 339
seq 9100 9400 | sed -En '/^9(.)\19$/p'
9119 9229 9339
^
) 及 匹配结束位置 ($
) 说明:-
/(.)\1/
匹配二个相同字符。 -
/^(.)\1/
匹配起始位置^
,开始的字符再接续一个相同的字符。 -
/(.)\1$/
匹配结束位置$
,回调匹配,最后的字符要跟前一个字符相同。 -
/^(.)\1$/
同时匹配开始及结束,在匹配一个字符的情况,只有两个相同的字符合乎匹配。
seq 87 110 | sed -En '/^(.)\1/p'
88
99
110
seq 87 110 | sed -En '/(.)\1$/p'
88
99
100
seq 87 111 | sed -En '/^(.)\1$/p'
88
99
若表达式中可匹配多个数量如 *
、?
,以 /(.*),\1/
为例,将两组文本以字符 ,
区分。
若分组表达式无其他条件,则分组匹配内容往往是 0 个字符,则匹配有字符 ,
的行。
/(.*),\1/
匹配有字符 ,
的行printf '%s\n' 1,1 12,123 321,21 12345 | sed -En '/(.*),\1/p' 1,1 12,123 321,21
/^(.*),\1/
匹配第一组开始的文本跟第二组开始的文本。printf '%s\n' 1,1 12,123 321,21 | sed -En '/^(.*),\1/p'
1,1
12,123
/(.*),\1$/
回调匹配第二组文本跟第一组文本 (字符 ,
之前)。printf '%s\n' 1,1 12,123 321,21 | sed -En '/(.*),\1$/p'
1,1
321,21
/^(.*),\1$/
同时匹配开始及结束,匹配两组相同的文本。printf '%s\n' 1,1 12,123 321,21 | sed -En '/^(.*),\1$/p'
1,1
sed 基本命令
|
添加文本,在行后添加文本 (文本在下一行)。 |
|
取代文本,用文本取代行。 |
|
删除。 |
|
插入文本,在行之前插入文本 (文本在上一行)。 |
|
以明确形式打印 (打印控制字符)。 |
|
读取下一行。 |
|
打印。 |
|
离开。 |
|
读取文件。 |
|
替换 (搜索并替换)。 |
|
写到文件。 |
|
转换字符。 |
|
打印行号。 |
a
(添加)、i
(插入)
- a text
-
在行后添加一行文本,输入的文本直到换行。自动忽略
a
命令后跟文本间的空格。在第二行后添加 hello:seq 3 | sed '2a hello'
1 2 hello 3
如果换行要接续则输入「行接续字符
\
(反斜线)」。在第二行后添加一行 hello 再接续 world:seq 3 | sed '2a hello\ world ' (1)
1 亦可采用「 \n
换行」如seq 3 | sed '2a hello\nworld'
1 2 hello world 3
在第二行后添加 hello,在第三行添加 world:seq 3 | sed '2a hello 3a world '
1 2 hello 3 world
- i text
-
在行前插入一行文本,输入的文本直到换行。自动忽略
i
命令后跟文本间的空格。在第二行前插入 hello:seq 3 | sed '2i hello'
1 hello 2 3
如果换行要接续则输入「行接续字符
\
(反斜线)」。在第二行前插入一行 hello 再接续 world:seq 3 | sed '2i hello\ world ' (1)
1 亦可采用「 \n
换行」如seq 3 | sed '2i hello\nworld'
1 hello world 2 3
在第二行前插入 hello,在第三行前插入 world:seq 3 | sed '2i hello 3i world '
1 hello 2 world 3
- a\
text -
在行后添加文本,在新行添加文本,直到换行。忽略
a\
之间的空格。在第二行后添加 hello (前置一个空格):seq 3 | sed '2a\ hello'
1 2 hello 3
如果换行要接续则输入「行接续字符\
(反斜线)」:seq 3 | sed '2a\ hello\ world s/./x/' (1)
1 本例再增加替换命令,替换 (一行只有) 一个任意字符为 x
。x x hello world x
a\
命令和文本可以分为两个-e
参数,使编写脚本更加容易:seq 3 | sed -e '2a\' -e ' hello\nworld' -e 's/./x/'
- i\
text -
在行前插入文本,在新行输入文本,直到换行。忽略
i\
之间的空格。在第二行前插入 hello (前置一个空格):seq 3 | sed '2i\ hello'
1 hello 2 3
如果换行要接续则输入「行接续字符\
(反斜线)」:seq 3 | sed '2i\ hello\ world s/./x/'
x hello world x x
i\
命令和文本可以分为两个-e
参数,使编写脚本更加容易:seq 3 | sed -e '2i\' -e ' hello\nworld' -e 's/./x/'
c
(取代)
用文本取代行,输入的文本直到换行。自动忽略 c
命令后跟文本间的空格。
seq 3 | sed '2c hello'
1 hello 3
如果换行要接续则输入「行接续字符 \
(反斜线)」。
seq 3 | sed '2c hello\
world (1)
'
1 | 亦可采用「\n 换行」如 seq 3 | sed '2c hello\nworld' |
1 hello world 3
seq 3 | sed '2c hello
3c world
'
1 hello world
- c\
text -
用文本取代行,输入的文本直到换行。忽略
c\
之间的空格。在第二行取代成 hello (前置一个空格):seq 3 | sed '2c\ hello'
1 hello 3
如果换行要接续则输入「行接续字符
\
(反斜线)」。seq 3 | sed '2c\ hello\ world s/./x/'
x hello world x
c\
命令和文本可以分为两个-e
参数,使编写脚本更加容易:seq 3 | sed -e '2c\' -e ' hello\nworld' -e 's/./x/'
x hello world x
n
(读取下一行)
seq 10 | sed -n 'n;n;p'
sed 先读取第 1 行,再读取 2 行,第 3 行打印。下一个循环 (cycle),由第 4 行开始,读取 2 行,第 6 行 打印,再次运行下一个循环。
3 6 9
q
、Q
(离开)
离开 sed 不再处理任何命令或输入。
- 命令格式
-
q
[exit-code]Q
[exit-code]
Q
命令和 q
一样,但 Q
离开时不会打印。
seq 3 | sed 'n;q'
1 2
seq 3 | sed 'n;q255' ; echo $? (1)
1 | 由 echo $? 打印回传代号。 |
1 2 255
/etc/passwd
的前 10 行sed '10q' /etc/passwd
s
(替换)
替换命令格式 s/pattern/replacement/flags
|
全局更改 |
|
不区分大小写。 |
|
多行模式 (multi-line mode)。 |
|
打印 |
|
写入文件 (多个 flag 时,需放在最后) |
|
第 number 次匹配时,运行替换。 |
flags 可用 (不定数量的) 空格字符区分。
- flags 示例
-
number
-
取代 a 为 X:
echo BAnana | sed 's/a/X/' BAnXna
取代第 2 个 a 为 X (flagsnumber
为 2):echo BAnana | sed 's/a/X/2' BAnanX
g
-
以 flags
g
(全局更改) 全部取代 a 为 X:echo BAnana | sed 's/a/X/g' BAnXnX
i, I
-
以 flags
g
及i
(不区分大小写) 取代 a 为 X:echo BAnana |sed 's/a/X/gi' BXnXnX
p
-
sed 采用
-n
命令行选项不自动输出:echo BAnana |sed -n 's/a/X/'
无输出
由s
命令采用 flagsp
(打印) 输出:echo BAnana |sed -n 's/a/X/p' BAnXna
w
-
由
s
命令采用 flagsw
(写入) 到标准输出:echo BAnana |sed -n 's/a/X/w /dev/stdout' BAnXna
m, M
-
在说明多行模式 (multi-line mode) 之前,先准备好多行的文本内容。
sed 先读取第 1 行, 运行 2 次命令
N
(读取下一行),读取后的「文本内容」会有 3 行。
注:「文本内容」称为「模式空间 (pattern space)」。seq 3 | sed 'N;N'
1 2 3
s
替换命令缺省行为-
sed 缺省行为将模式空间中所有的数据一起处理,整个数据只有一个开始 (
^
) 和结束($
),而匹配一个任意字符 (.
) 也是整个数据一起匹配。替换「开始 (^
)」为^
,替换「结束 ($
) 」为$
seq 3 | sed 'N;N; s/^/\^/g; s/$/\$/g'
^1 2 3$
任意数量的字符 (.*
) 加上中括号seq 3 | sed 'N;N; s/.*/\[&\]/g'
[1 2 3]
s
替换命令多行模式-
在多行模式 (multi-line mode) 下,
^
、$
表示每一行的开始和结束,而.
只会匹配同一行。替换「开始 (^
)」为^
,替换「结束 ($
) 」为$
seq 3 | sed 'N;N; s/^/\^/mg; s/$/\$/mg'
^1$ ^2$ ^3$
任意数量的字符 (.*
) 加上中括号seq 3 | sed 'N;N; s/.*/\[&\]/mg'
[1] [2] [3]
|
将「替换文本 (replacement)」转成小写,直到 |
|
将第一个字符转为小写。 |
|
将替换转成大写,直到 |
|
将第一个字符转成大写。 |
|
停止 (由 |
\U
示例-
匹配前置任意字符的 at,将合乎匹配的文本 (
&
) 转成大写 (\U
)。echo 'bat monkey and cat' | sed 's/.at/\U&/g'
BAT monkey and CAT
\u
示例-
匹配前置任意字符的 at,将合乎匹配的文本 (
&
) 第一个字改成大写(\u
)。echo 'bat monkey and cat' | sed 's/.at/\u&/g'
Bat monkey and Cat
y
(转换字符)
y/原始字符集/目的字符集/
替原始字符集的个数和目的字符集个数必须相等,字符集内每个字符一一对应。
a-j
至 0-9
:echo hi mary | sed 'y/abcdefghij/0123456789/' 78 m0ry
l
(明确形式打印)
- 明确形式打印
-
无法打印的字符 (如换行
\n
) 和字符\
以 C 风格的形式转译;超出长度的尾端会以「接续字符\
」拆分,末尾会标示$
。
注:字符$
是由命令l
明确表达,该字符实际上并不存在。
可由命令选项 l N
或命令行选项 sed -l N
、sed --line-length=N
指定输出最大长度 N。
echo -e 'Hello\tworld' | sed -n 'l'
Hello\tworld$
echo -e 'Hello\tworld' | sed -n 'l8'
-l
限制长度 8echo -e 'Hello\tworld' | sed -l8 -n -e 'l'
Hello\t\ (1) world$
1 | 当长度不足时,尾端会打印「接续字符 \ 」。 |
下列可将 将换行替换为 ,
seq 3 | sed -z 's/\n/,/g'
但为什么? 因为命令行选项 -z
会将所有的换行,变成一行文本。
z
的行为seq 3 | sed -zn 'l' (1)
1 | 由于运行了明确打印 (l ),以命令行选项 (-n ) 禁止「缺省打印」,避免混淆。 |
1\n2\n3\n$
sed 高端命令
在说明高端命令之前,先说明「模式空间 (pattern space
)」。
sed 的缺省动作为一次读取一行到「模式空间」,然后运行指令,接着处理下一行;继续「循环 (cycle)」直到文件结束 (EOF)。
d
删除命令是指删除「模式空间」,p
打印命令是指打印「模式空间」,sed 的「缺省打印」(命令行选项无 -n
) 也是打印 (在处理所有指令后剩下的) 「模式空间」。
- sed 高端命令
-
d
删除模式空间,然后重启循环 (cycle)。
D
删除模式空间内的第一行,并以模式空间 (删除后) 的结果「重启指令 (script)」。
注:如果 (删除前的) 模式空间不含换行,则为一般循环跟d
命令相同 (会读取新行)。n
(读取) 下一行拷贝至模式空间 next (copy),「缺省打印」模式空间。
N
(读取) 下一行添加至模式空间 Next (append)。
p
p (小写) 打印模式空间。
P
P (大写) 打印模式空间内的第一行。
q
离开,「缺省打印」(目前的) 模式空间。
Q
离开 (不打印)。
z
清除模式空间。
{}
命令组,一次运行多个命令,命令组格式为
{ 命令 [;命令] }
。#
注解
!
否定 (反动作)
注:命令行有
-n
选项时,则不会「缺省打印」。- 高端命令补充
-
D
命令-
删除模式空间内的第一行,剩余第二行(含以后),若模式空间只有一行则全部删除,另一种说法是「删除模式空间的内容直到第一个换行字符,若无换行时则删除全部」。
本节中将D
的后续动作称为「重启指令」(重新运行指令),不采用通用的说法「重启循环」,避免混淆。重启指令时,sed 并不会读取新的输入行,而是按照指令运行动作。 n
命令-
读取下一行拷贝至模式空间,先前的「模式空间」将会消失,该命令可「缺省打印」先前 (未读取前) 的「模式空间」。
N
命令-
读取下一行添加至模式空间,另一种说法是「先添加换行至模式空间,再将读取行添加至模式空间」。
模式空间的结果为:「目前 (上次) 的模式空间」 +\n
+ 读取行 (本次读取的模式空间)」。 P
(大写) 命令-
打印模式空间内的第一行,若模式空间只有一行也会打印,另一种说法是「打印模式空间内的第一个字直到换行字符,若无换行则打印全部」。
seq 5 | sed 'n;l;d'
1 2$ 3 4$ 5
- 流程说明
-
-
在每个循环的开始,sed 读取一行至模式空间 (在第一循环为
1
)。 -
命令
n
「缺省打印」上次模式空间(在第一循环为 1),命令n
读取下一行至模式空间 (在第一循环为2
)。 -
命令
l
明确打印模式空间时加入尾端标示 (在第一循环为 2$)。 -
命令
d
重启循环。 -
在下一个循环中,sed 读取一行至模式空间 (例如
3
),命令n
读取下一行至模式空间 (例如4
)。
-
seq 5 | sed -n 'n;p;d'
2 4
在上述的「流程说明」步骤说明如下
|
n;p
为下一行打印,那么打印 3 倍数行则为「下一行再下一行然后打印」,指令为 n;n;p
(亦可采用 选择行号 如 0~3 p
)。
打印单数行为「先打印再读取下一行」,指令为 p;n
(或采用选择行号的 1~2 p
)。
--debug
选项,可得知进程情况seq 5 | sed -n 'n;p' --debug
SED PROGRAM: n p INPUT: 'STDIN' line 1 PATTERN: 1 COMMAND: n PATTERN: 2 COMMAND: p 2 END-OF-CYCLE: INPUT: 'STDIN' line 3 PATTERN: 3 COMMAND: n PATTERN: 4 COMMAND: p 4 END-OF-CYCLE: INPUT: 'STDIN' line 5 PATTERN: 5 COMMAND: n (1) END-OF-CYCLE:
1 | 打印双数行在打印第 4 行后的循环;在第 5 行运行 n;p 时,在运行 n 命令之后已经 EOF ,sed 会中断循环并结束。 |
多行处理
printf '%s\n' a b c | sed = | sed -e 'N; s/\n/ /'
1 a 2 b 3 c
-
第一个 sed,先将每一行,打印行号 (
=
) 成为二行,行号在第一行,原始内容的第一行变成第二行。 -
第二个 sed,以命令
N
将两行合并成一行,两行中间为换行 (\n
),再以命令s
将换行替换为空格。
N; s/\n/ /
流程说明-
-
在每个循环的开始,sed 读取一行至模式空间 (在第一循环为
1
)。 -
命令 N 读取下一行 (第一循环为
a
),添加至模式空间 (第一循环为1
、\n
、a
)。 -
命令
s
将换行替换为空格 (第一循环为1 a
),运行后「缺省打印」1 a。 -
非文件结束 (EOF) 时继续循环。
-
在下一个循环中,sed 读取一行至模式空间 (例如
2
),命令N
读取下一行 (例如b
),添加至模式空间 (例如2
、\n
、b
)。
-
seq 5 | sed 'N;l;D'
1\n2$ 2\n3$ 3\n4$ 4\n5$ 5
-
sed 将第一行读入模式空间(即为
1
)。 -
在每个进程的开始,命令
N
添加至模式空间(第一个流程即为1
、\n
、2
)。 -
命令
l
打印模式空间的内容 (第一个流程即为 1\n2$)。 -
命令
D
删除模式空间内的第一行(在第一个流程结束时模式空间留下2
)。
然后「重启进程」 (并不会读取新行,由第 2 步骤开始运行)。 -
在下一个流程中,命令
N
添加至模式空间(例如2
、\n
、3
)。 -
整个进程只有一个循环,循环结束时模式空间留下 5。
printf '%s\n' 1A 2B 2B 3C | sed -n '$!N; /^\(.*\)\n\1$/!P; D' &&\
printf '%s\n' 1A 2B 2B 3C | sed -En '$!N; /^(.*)\n\1$/!P; D'
1A 2B 3C 1A 2B 3C
printf '%s\n' 1A 2B 2B 3C | sed -n '$!N; /^\(.*\)\n\1$/!l; D'
1A\n2B$ 2B\n3C$ 3C$
-
sed 将第一行读入模式空间(即为
1A
)。 -
在每个进程的开始,命令
$!N
在非最后行时添加至模式空间(第一个流程即为1A
、\n
、2B
)。 -
/^\(.*\)\n\1$/
将两组文本以\n
区分,匹配两组相同文本的「行号」,!l
行号不匹配时运行明确打印(第一个流程打印1A\n2B$
,若命令为P
则打印1A
)。 -
命令
D
删除模式空间内的第一行(在第一个流程结束时模式空间留下2B
),然后「重启进程」。 -
在第二个流程中命令
$!N
添加模式空间(例如2B
、\n
、2B
),行号匹配不运行!l
,命令D
(模式空间留下2B
)。 -
在第三个流程中命令
$!N
添加模式空间(例如2B
、\n
、3C
),行号不匹配运行!l
(第三个流程打印2B\n3C$
),命令D
(模式空间留下3C
)。 -
在第四个流程中,已为最后一行不运行
$!N
(模式空间为3C
),行号不匹配运行!l
(第四个流程打印3C$
)。 -
运行命令
D
时,模式空间不含换行字符为一般循环并且清除模式空间,sed 结束进程。
(
cat << EOF
Name:John
Year-old:38
Name:Marry
Year-old:43
Name:Helen
Year-old:38
EOF
) |
sed -n '/Name/ {
N
/Year-old:38/ !d
P
}'
Name:John Name:Helen
-
/Name/
匹配行号Name
。 -
N
添加模式空间(第一个流程即为Name:John
、\n
、Year-old:38
)。 -
/Year-old:38/
匹配行号年龄为 38,若不是则运行d
重启循环。 -
P
打印模式空间的第一行(第一个流程打印Name:John
)。 -
在下一个循环中,年龄为 43,行号不匹配,运行
d
重启循环。
空间操作
|
将模式空间拷贝到保持空间 hold (copy)。 |
|
将模式空间添加至保持空间 Hold (append)。 |
|
将保持空间拷贝到模式空间 get pattern space (copy)。 |
|
将保持空间添加至模式空间 Get pattern space (append)。 |
|
交换空间 (交换保持空间和模式空间互换) exchange。 |
- 空间操作命令补充
-
H
命令-
将模式空间添加至保持空间,另一种说法是「添加换行至保持空间,再将模式空间添加至保持空间」。
保持空间的结果为:「目前 (上次) 的保持空间」 +\n
+ 模式空间」。 G
命令-
将保持空间添加至模式空间,另一种说法是「添加换行至模式空间,再将保持空间添加至模式空间」。
模式空间的结果为:「目前 (上次) 的模式空间」 +\n
+ 保持空间」。
echo -e '1\n\n3\n4\n5' | sed -n '/./{H;$!d}; x; l'
\n1$ \n3\n4\n5$
第一个表达式 /./{H;$!d}
对所有具值的 (非空) 行运行「命令组」,将 (当前的) 模式空间添加到保持空间,非最后一行时删除模式空间并重新循环。
「选择行号」后面只能接一个命令,采用「{}
命令组」,将多个命令「组合」成一个。
-
第 1 行具值,模式空间为
1
添加到保持空间 (H
) 为\n1
(保持空间初始为空,添加至下一行)。非最后行 ($!
),由d
删除模式空间并重启循环。 -
第 2 行是空行,第一个表达式不会运行动作。
再来 sed 运行交换空间 (x
),将保持空间 (\n1
) 跟模式空间 (NUL
) 互换,互换后;保持空间为NUL
,模式空间为\n1
。
再明确打印模式空间 (l
) 为 \n1$。 -
第 3 行具值,模式空间为
3
,命令H
添加保留空间后为\n3
(在前一步骤保持空间为NUL
),非最后行重启循环。 -
第 4 行具值,模式空间为
4
,命令H
添加保留空间后为\n3\n4
,非最后行重启循环。 -
第 5 行具值,模式空间为
5
,命令H
添加保留空间后为\n3\n4\n5
,最后行不重启循环。 -
再来 sed 运行
x; l
输出 \n3\n4\n5$ 后结束进程。
echo -e '1\n\n3\n4\n5' | sed '/./{H;$!d}; x; s/^/\nSTART-->/; s/$/\n<--END/'
START--> 1 <--END START--> 3 4 5 <--END
如何倒序?
采用 G
将保持空间添加至模式空间,当保持空间是前一行时,添加后则为反排。(先添加再保留)。
seq 3 | sed -n 'G;h;l' (1)
1 | 在测试 sed 的命令时,跟前例一样;以 l 明确打印比较清楚。 |
1\n$ 2\n1\n$ 3\n2\n1\n$
但每次打印,并不正确。
seq 3 | sed -n 'G;h;$l'
3\n2\n1\n$
大致正确,但上述的第一行也加入换行 (因为是反排,所以是最后字符)。
seq 3 | sed -n '1! G; h; $l'
3\n2\n1$
seq 3 | sed -n '1h; 2,${G;h}; $l'
分支
分支 (branch),意思是跳跃 (jump) 或者是 go to。
- sed 分支命令
-
:label
定义 label 为一个或多个字母作为标签。
b
(无条件) 分支。
t
s
命令合乎匹配时分支。T
s
命令不匹配时分支。b
、t
和T
命令后面可接标签,命令跟标签之间可为 0 个或多个空白。分支至标签时依据指令运行动作,但如果省略标签,则分支命令会重启循环 (sed 的缺省动作会读取新的输入行)。 - 千位分隔符号范例
-
确定替换命令 (
s
) 正确处理千位符号echo '12345678' | sed 's/\B[0-9]\{3\}\b/,&/' &&\ echo '12345678' | sed -E 's/\B[0-9]{3}\b/,&/' &&\ echo '123' | sed 's/\B[0-9]\{3\}\b/,&/' &&\ echo '123' | sed -E 's/\B[0-9]{3}\b/,&/'
12345,678 12345,678 123 123
以分支完成所有的千位符号echo '12345678' | sed ':X s/\B[0-9]\{3\}\b/,&/; t X' &&\ echo '12345678' | sed -E ':X s/\B[0-9]{3}\b/,&/; t X'
12,345,678 12,345,678
流程如下:-
第一次匹配
678
,模式空间为12345,678
,合乎匹配分支到 (t
) 标签 X,继续匹配。 -
第二次匹配
345
,模式空间为12,345,678
,合乎匹配分支到标签 X 并继续。 -
第三次不合乎匹配,不分支并结束进程。
以分支运行匹配条件错误范例 echo '12345678' | sed ':X s/[0-9]{3}\b/,&/; t X' --debug
流程如下:-
第 1 次匹配
678
,模式空间为12345,678
,合乎匹配分支到 (t
) 标签 X,继续匹配。 -
第 2 次匹配
345
,模式空间为12,345,678
,合乎匹配分支到标签 X 并继续。 -
第 3 次匹配
345
,模式空间为12,,345,678
,合乎匹配分支到标签 X 并继续。 -
第 4 次匹配
345
,模式空间为12,,,345,678
,合乎匹配分支到标签 X 并继续。 -
sed 一直在运行没有结束,这表示了发生了「无穷循环 (infinite loop)」,可采用命令行选项
--debug
来调试。
由上述的第 3 次流程中已知,匹配条件有错误。当发生无穷循环时,应先确定替换命令 (
s
) 有正确处理。替换命令匹配条件错误 (参阅:正规表示法-边界):echo ',345,' | sed 's/[0-9]{3}\b/,&/'
,,345,
-
将所有行合并至模式空间。
seq 3 | sed -n ':X; N; $! bX; l'
1\n2\n3$
-
:X
创建标签X
-
N
下一行添加至模式空间 -
$!
非最后一行,bX
(无条件) 分支到标签X
,继续运行指令。
-z
seq 3 | sed -nz 'l'
1\n2\n3\n$
,
seq 3 | sed -z 's/\n/,/g' &&\
seq 3 | sed ':X; N; $!bX; s/\n/,/g'
1,2,3,1,2,3
读写命令 (rRwW)
|
读取整个文件,插入到输出流中。 |
|
依次读取文件一行,插入到输出流中。 |
|
将模式空间写入文件。 |
|
将模式空间内的第一行写入文件。 |
r
、R
-
在循环结束时或在读取下一个输入时,将文件内容插入到输出流中。
-
不会影响模式空间
-
一律打印,忽略命令行选项
-n
。 -
若无法读取文件,则将其视为一个空档,并不会出现错误。
读取整个文件 (命令r
),如输出/etc/passwd
3 次seq 3 | sed -n 'r /etc/passwd'
依次读取文件一行 (命令R
),如输出/etc/passwd
前 3 行seq 3 | sed -n 'R /etc/passwd'
-
w
、W
-
打开的文件会不断添加,直到 sed 命令结束。
将模式空间写入 (命令w
) 到标准输出seq 3 | sed -n 'N; N; w /dev/stdout'
1 2 3
将模式空间内的第一行写入 (命令W
) 到标准输出,如模式空间每次读取 3 行:seq 9 | sed -n 'N; N; W /dev/stdout'
1 4 7
采用命令w
输出 1 4 7。seq 9 | sed -n -e 'w /dev/stdout' -e 'n;n'
在文件名称之后的命令,必须使用选项
-e
分开表达式。
测试「特殊字符」转译
确定哪些「特殊字符」在 BRE、ERE 需要以转义字符 (\
) 转译。
specChar='a'
echo "3${specChar}14" \| sed -e "s/3\\${specChar}14/3\.14159/" &&\
echo "3${specChar}14" \| sed -e "s/3${specChar}14/3.14159/" &&\
echo "3${specChar}14" \| sed -E "s/3\\${specChar}14/3\.14159/" &&\
echo "3${specChar}14" \| sed -E "s/3${specChar}14/3.14159/"
3a14 | sed -e s/3\a14/3\.14159/ 3a14 | sed -e s/3a14/3.14159/ 3a14 | sed -E s/3\a14/3\.14159/ 3a14 | sed -E s/3a14/3.14159/
echo "3${specChar}14" | sed -e "s/3\\${specChar}14/3\.14159/" &&\
echo "3${specChar}14" | sed -e "s/3${specChar}14/3.14159/" &&\
echo "3${specChar}14" | sed -E "s/3\\${specChar}14/3\.14159/" &&\
echo "3${specChar}14" | sed -E "s/3${specChar}14/3.14159/"
3a14 (BRE 转译) 3.14159 (BRE 不转译) 3a14 (ERE 转译) 3.14159 (ERE 不转译)
!
@
#
%
&
,
-
"
=
」:specChar='!'
specChar='@'
specChar='#'
specChar='%'
specChar='&'
specChar='_'
specChar=','
specChar='-'
specChar='"'
specChar='='
3.14159 (BRE 转译)
3.14159 (BRE 不转译)
3.14159 (ERE 转译)
3.14159 (ERE 不转译)
.
$
*
^
/
\
[
]
」:specChar='.'
3.14159 (BRE 转译)
3.14159 (BRE 不转译)
3.14159 (ERE 转译)
3.14159 (ERE 不转译)
不转译也正确,但因为「.」为匹配字符,应纳入「BRE ERE 一律要转译的字符」。
specChar='$'
3.14159 (BRE 转译)
3.14159 (BRE 不转译)
3.14159 (ERE 转译)
3$14 (ERE 不转译)
specChar='*'
3.14159
3*3.14159
3.14159
3*3.14159
specChar='^'
3.14159
3.14159
3.14159
3^14
specChar='/' (定界字符)
3.14159 (BRE 转译)
Error (BRE 不转译)
3.14159 (ERE 转译)
Error (ERE 不转译)
specChar='\'
3.14159
Error
3.14159
Error
specChar='['
3.14159
Error
3.14159
Error
specChar=']'
3.14159
3.14159
3.14159
3.14159
BRE ERE 不转译跟转译后结果相同,但为了配合「[」,将「]」纳入「BRE ERE 一律要转译的字符」。
/
),将 sed 的定界字符改为 _
。specChar='/'
echo "3${specChar}14" | sed -e "s_3\\${specChar}14_3\.14159_" &&\
echo "3${specChar}14" | sed -e "s_3${specChar}14_3.14159_" &&\
echo "3${specChar}14" | sed -E "s_3\\${specChar}14/3_.14159_" &&\
echo "3${specChar}14" | sed -E "s_3${specChar}14_3.14159_"
3.14159 (BRE 转译)
3.14159 (BRE 不转译)
3/14 (ERE 转译)
3.14159 (ERE 不转译)
`
'
<
>
」:specChar='`'
3`14 (BRE 转译)
3.14159 (BRE 不转译)
3`14 (ERE 转译)
3.14159 (ERE 不转译)
specChar="'"
3'14
3.14159
3'14
3.14159
specChar='<'
3<14
3.14159
3<14
3.14159
specChar='>'
3>14
3.14159
3>14
3.14159
+
?
|
(
)
{
}
」:specChar='+'
3+14 (BRE 转译)
3.14159 (BRE 不转译)
3.14159 (ERE 转译)
3+14 (ERE 不转译)
specChar='?'
3?3.14159
3.14159
3.14159
3?3.14159
specChar='|'
3.14159|14
3.14159
3.14159
3.14159|14
specChar='('
Error
3.14159
3.14159
Error
specChar=')'
Error
3.14159
3.14159
Error
specChar='{'
Error
3.14159
3.14159
Error
specChar='}'
3.14159
3.14159
3.14159
3.14159
BRE ERE 不转译跟转译后结果相同,但为了配合「{」,将「}」纳入「BRE 不转译 ERE 要转译的字符」。