![cygwin上的sed只能替换一个字符?](https://linux22.com/image/107909/cygwin%E4%B8%8A%E7%9A%84sed%E5%8F%AA%E8%83%BD%E6%9B%BF%E6%8D%A2%E4%B8%80%E4%B8%AA%E5%AD%97%E7%AC%A6%EF%BC%9F.png)
我正在尝试使用 sed 和 cygwin 替换 Windows 上 20 多个文件中的 XML 元素。该行是:
cd "D:\Backups\Tasks"
sed -i 's~<StartWhenAvailable>true</StartWhenAvailable>~<StartWhenAvailable>false</StartWhenAvailable>~g' "Task_01.xml"
这不能代替任何东西。但是,如果我尝试:
sed 's~<~[~g' "Task_01.xml"
它输出:
[AllowHardTerminate>true[/AllowHardTerminate>
[StartWhenAvailable>true[/StartWhenAvailable>
[RunOnlyIfNetworkAvailable>false[/RunOnlyIfNetworkAvailable>
但是,如果我尝试仅添加一个字符,它只会按原样输出文档:
sed 's~<B~[B~g' "Task_01.xml"
上面什么也没做。我究竟做错了什么?雪佛龙是特殊字符还是我误用了 sed?或者是cygwin的错误?
答案1
最有可能的是,该文件采用 UTF-16 编码,即每个字符 2 或 4 个字节,甚至可能在开头带有字节顺序标记。
示例中显示的字符(所有 ASCII 字符)通常使用 2 个字节进行编码,其中第一个或第二个字节(取决于它是 big-enfian 还是 little-endian UTF-16 编码)为 0,另一个为 0是 ASCII/Unicode 代码。 0 字节通常在终端上不可见,因此当转储到那里时,文本显示正常,因为其余部分只是 ASCII,但实际上文本包含:
<[NUL]S[NUL]t[NUL]a[NUL]r[NUL]t[NUL]W[NUL]h[NUL]e[NUL]n[NUL]...
您需要将该文本转换为您所在区域的字符集才能sed
处理它。请注意,UTF-16 不能用作 Unix 区域设置中的字符编码。您不会找到使用 UTF-16 作为其字符编码的区域设置。
iconv -f utf-16 < Task_01.xml |
sed 's~<StartWhenAvailable>true</StartWhenAvailable>~<StartWhenAvailable>false</StartWhenAvailable>~g' |
iconv -t utf-16 > Task_01.xml.out
假设输入有 BOM。如果不是,您需要确定它是大端还是小端(可能是小端)并将其更改utf-16
为utf-16le
or utf-16be
。
如果语言环境的字符集是 UTF-8,即使文本包含非 ASCII 字符,翻译过程中也不应该丢失任何内容。
由于 Cygwinsed
通常是 GNU sed
,因此它也能够自行处理该类型的二进制输入(因为它包含 NUL 字节),因此您还可以执行以下操作:
LC_ALL=C sed -i 's/t\x00r\x00u\x00e/f\x00a\x00l\x00s\x00e/g' Task_01.xml
该file
命令应该能够告诉您输入是否确实是 UTF-16。您可以使用sed -n l
或od -tc
查看那些隐藏的 NUL 字符。带 BOM 的小端 UTF-16 文本示例:
$ echo true | iconv -t utf-16 | od -tc
0000000 377 376 t \0 r \0 u \0 e \0 \n \0
0000014
$ echo true | iconv -t utf-16 | sed -n l
\377\376t\000r\000u\000e\000$
\000$
$ echo true | iconv -t utf-16 | file -
/dev/stdin: Little-endian UTF-16 Unicode text, with no line terminators
要使用zsh
//处理多个文件:bash
ksh93
set -o pipefail
for file in ./*.xml; do
cp -ai "$file" "$file.bak" &&
iconv -f utf-16 < "$file.bak" |
sed 's~<StartWhenAvailable>true</StartWhenAvailable>~<StartWhenAvailable>false</StartWhenAvailable>~g' |
iconv -t utf-16 > "$file" &&
rm -f "$file.bak"
done
答案2
将sed
命令放入文件中,例如 sed.cmds,然后调用sed
为:
sed -i -f "sed.cmds" "MyFile.xml"
还尝试将分隔符更改为_
,如下所示:
s_<BooleanTag>true</BooleanTag>_<BooleanTag>false</BooleanTag>_g