0

多くの構成データを含む「XML に似た」ファイルがあります。"]]>]]> で区切られた 3 つの XML ファイルを連結したようなものなので、「XML に似ている」と言います。

例えば

<?xml version="1.0" encoding="UTF-8"?>
<hello><world>"Earth"</world></hello>]]>]]><?xml version="1.0" encoding="UTF-8"?>
<data><lemur><type>"Ring-tailed"</type></lemur></data>]]>]]><?xml version="1.0" encoding="UTF-8"?>
<data><lemur><type>"Mouse"</type></lemur></data>]]>]]>

ファイル内のすべての XML タグをインデントするために xmllint を呼び出すスクリプトを作成しようとしています。ただし、xmllint (および他の多くの xml 書式設定プログラム) では、ファイル内に XML ドキュメントが 1 つだけ存在する必要があるようです。たとえば、ファイルは " " で始まり、<?xml version="1.0" encoding="UTF-8"?>ルート ツリーが 1 つだけ含まれている必要があります。

そこで、データを個別のチャンクに解析して xmllint に渡す awk スクリプトを作成しようとしましたが、通過できないというエラーが発生しています。スクリプトと出力を以下に示します。

$ awk '
BEGIN {
    RS = "]]>]]>"
    xmlFormatCommand = "xmllint --format -"
} 

{
    print $0 | xmlFormatCommand 
}
' SmallTest.xml

-:3: parser error : XML declaration allowed only at the start of the document
<?xml version="1.0" encoding="UTF-8"?>
     ^
-:4: parser error : Extra content at the end of the document
<data><lemur><type>"Ring-tailed"</type></lemur></data>
^

awk が 3 つの一時ファイルに出力する操作と、それらのファイルに対して xmllint を操作する操作の 2 つの操作で実行すると、うまくいきます。

例えば

awk 'BEGIN {RS = "]]>]]>"} {print $0 > "Section_" NR ".txt" }' SmallTest.xml

その結果、Section_1.txt、Section_2.txt、および Section_3.txt の 3 つのファイルが作成されます。Section_2.txt の内容は次のとおりです。

$ cat Section_2.txt
<?xml version="1.0" encoding="UTF-8"?>
<data><lemur><type>"Ring-tailed"</type></lemur></data>

そのファイルを xmllint でフォーマットできます。

$ cat Section_2.txt | xmllint --format -
<?xml version="1.0" encoding="UTF-8"?>
<data>
  <lemur>
    <type>"Ring-tailed"</type>
  </lemur>
</data>

したがって、awkスクリプトの最初の場所でxmllintにパイプできない理由がわかりません。

あなたが提供できる助けに感謝します。

-ジョン

4

2 に答える 2

1

あなたの問題は、一言で言えば、 awk が同じパイプを使い続けることです。パイプは、パイプを開いたときとまったく同じ文字列で記憶され (つまり、まったく同じコマンドを同時に 2 回実行することはできません)、レコードが次々にパイプに書き込まれるため、xmllintプロセスは 1 つだけです。ファイル全体を入力として取得します。

これは、すべてのレコードの後に​​パイプを閉じることで修正できます。

$ awk '
BEGIN {
    RS = "]]>]]>"
    xmlFormatCommand = "xmllint --format -"
} 

{
    print $0 | xmlFormatCommand 
    close(xmlFormatCommand)      # <-- HERE
}
' SmallTest.xml

ここでcloseは、パイプが記憶されている識別子 (コマンド) を引数として受け入れます。これは他のプログラミング言語と比べて奇妙に見えることは承知しています。

ちなみに、質問のファイルの最後に空のレコードがあるため、そのような空のレコードを除外する条件をそこに入れたい場合があります。例えば、

$ awk '
BEGIN {
    RS = "]]>]]>"
    xmlFormatCommand = "xmllint --format -"
} 

! /^\s*$/ {  # <-- HERE
    print $0 | xmlFormatCommand 
    close(xmlFormatCommand)
}
' SmallTest.xml

where/^\s*$/は、先頭と末尾の間に空白のみがあるレコードに一致し、一致するものを!反転します。

于 2015-01-21T16:07:49.183 に答える
1

これは、print コマンドの出力が xmllint の同じインスタンスに送信され続けるためです。

これを解決する最も簡単な方法は、xmllint を使用して出力ファイルを作成することです。

awk '
    BEGIN {
    RS = "]]>]]>"
} 
{
    print $0 | "xmllint --format --output sample_"NR".xml -"
}
' SmallTest.xml

これを行うと、1 つのエラーが残ります。これは、xmllint が最後の行の後に 1 回呼び出され、入力が残っていないためです。そのため、ソース xml の最後の区切り文字を削除するか、awk に $0 の値があるかどうかを確認することができます。脚本。

すべてを stdout に出力するには、次のようにします。

awk '
BEGIN {
RS = "]]>]]>"
} 
{
print $0 | "xmllint --format -"
close("xmllint --format -")}
' SmallTest.xml

https://www.gnu.org/software/gawk/manual/html_node/Close-Files-And-Pipes.htmlを参照して ください

于 2015-01-21T16:20:31.750 に答える