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>
ただし、1文字だけを追加しようとすると、文書はそのまま出力されます。
sed 's~<B~[B~g' "Task_01.xml"
上記では何もしません。私は何が間違っていましたか? chevronは特殊文字ですか、それともsedを間違って使用していますか?それともcygwinのバグですか?
答え1
ほとんどの場合、ファイルはUTF-16でエンコードされており、文字ごとに2バイトまたは4バイトで、先頭にバイト順のマークが付いている可能性があります。
例に示されている文字(すべてのASCII文字)は通常、2バイトを使用してエンコードされます。ここで、1 番目または 2 番目のバイト (big-enfian または little-endian UTF-16 エンコードかどうかに応じて) は 0 で、残りの A 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
utf-16be
ロケールの文字セットがUTF-8の場合、テキストにASCII以外の文字が含まれていても、翻訳時に失われる内容はありません。
Cygwinはsed
通常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であるかどうかを通知できます。隠されたNUL文字を使用sed -n l
または表示できます。od -tc
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
答え2
sed
コマンドをsed.cmdsというファイルに入れて、次のようsed
に呼び出します。
sed -i -f "sed.cmds" "MyFile.xml"
また、次のように区切り記号をに変更してみました_
。
s_<BooleanTag>true</BooleanTag>_<BooleanTag>false</BooleanTag>_g