这是一个增强
wef’s answer.
我们可以去掉各种特殊字符的特殊含义问题
和字符串(^,.,[,*,$,\(,\),\{,@987654@30@,\?,@987
&、\1、……等等,还有 / 分隔符)
通过删除特殊字符。
具体来说,我们可以将所有内容都转换为十六进制;
那么我们只有0-9 和a-f 需要处理。
这个例子演示了原理:
$ echo -n '3.14' | xxd
0000000: 332e 3134 3.14
$ echo -n 'pi' | xxd
0000000: 7069 pi
$ echo '3.14 is a transcendental number. 3614 is an integer.' | xxd
0000000: 332e 3134 2069 7320 6120 7472 616e 7363 3.14 is a transc
0000010: 656e 6465 6e74 616c 206e 756d 6265 722e endental number.
0000020: 2020 3336 3134 2069 7320 616e 2069 6e74 3614 is an int
0000030: 6567 6572 2e0a eger..
$ echo "3.14 is a transcendental number. 3614 is an integer." | xxd -p \
| sed 's/332e3134/7069/g' | xxd -p -r
pi is a transcendental number. 3614 is an integer.
当然,sed 's/3.14/pi/g' 也会更改 3614。
上面的内容有点过于简单化了;它不考虑边界。
考虑这个(有点做作的)例子:
$ echo -n 'E' | xxd
0000000: 45 E
$ echo -n 'g' | xxd
0000000: 67 g
$ echo '$Q Eak!' | xxd
0000000: 2451 2045 616b 210a $Q Eak!.
$ echo '$Q Eak!' | xxd -p | sed 's/45/67/g' | xxd -p -r
&q gak!
因为$ (24) 和Q (51)
结合形成2<b><i>45</i></b>1,
s/45/67/g 命令将其从内部撕开。
它将2451 更改为2671,即&q (26 + 71)。
我们可以通过分隔搜索文本中的数据字节来防止这种情况,
替换文本和带空格的文件。
这是一个程式化的解决方案:
encode() {
xxd -p -- "$@" | sed 's/../& /g' | tr -d '\n'
}
decode() {
xxd -p -r -- "$@"
}
left=$( printf '%s' "$search" | encode)
right=$(printf '%s' "$replacement" | encode)
encode file.txt | sed "s/$left/$right/g" | decode
我定义了一个 encode 函数,因为我使用了该函数 3 次,
然后我为对称定义了decode。
如果您不想定义 decode 函数,只需将最后一行更改为
encode file.txt | sed "s/$left/$right/g" | xxd -p –r
请注意,encode 函数将数据(文本)的大小增加了三倍
在文件中,然后通过 sed 将其作为单行发送
- 最后甚至没有换行符。
GNU sed 似乎能够处理这个问题;
其他版本可能无法做到。
作为一个额外的好处,这个解决方案可以处理多行搜索和替换
(换句话说,搜索和替换包含换行符的字符串)。