Sed

cygwin 上的 sed 只能替換一個字元?

  • March 21, 2017

我正在嘗試使用 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有問題?

最有可能的是,該文件以 UTF-16 編碼,即每個字元有 2 或 4 個字節,甚至可能在開頭使用 Byte-Order-Mark。

範例中顯示的字元(所有 ASCII 字元)通常以 2 個字節編碼,其中第一個或第二個字節(取決於它是 big-enfian 還是 little-endian UTF-16 編碼)為 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-16utf-16leor 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 lod -tc查看那些隱藏的 NUL 字元。帶有 BOM 的 little-endian 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使用//處理多個文件bashksh93

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

引用自:https://unix.stackexchange.com/questions/352642