cygwin 上的 sed 只能替換一個字元?
我正在嘗試使用 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-16
為utf-16le
orutf-16be
。如果語言環境的字元集是 UTF-8,即使文本包含非 ASCII 字元,翻譯也不應該有任何失去。
由於 Cygwin
sed
通常是 GNUsed
,它也能夠自行處理那種類型的二進制輸入(因為它包含 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 的 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
使用//處理多個文件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