12,000以上のXMLファイルを含むフォルダーがあります。そのフォルダ内で特定の条件を満たすファイルのリストを取得する必要があります。
XMLファイルには、/BillingData/InvoiceLinesList/InvoiceLines
というノードがあります。 InvoiceLines
内に1つ以上のInvoiceLinesList
が存在する可能性があります。 InvoiceLines
で、値が<charge>
である99
というタグを検索する必要があります。また、同じInvoiceLines
に値がD
である<chargeType>
というタグがあります。 。
それを行うための最良の方法は何ですか? awk
を使用すると、それができるかもしれないと思いましたが、awk
が得意ではないため、複数の条件を検索する方法がわかりませんでした。ここでxmlstarlet
を使用する潜在的な方法を見ましたが、それは複数のタグで個別の値ではなく、単一のタグで1つまたは他の値のみを検索します。
awk
とsed
はどちらも、一般にXML(およびJSONやYAMLなどの他の同様の形式)の解析には適していません。たとえば、このXMLの例では、InvoiceLines
内のノードが格納される順序や、改行で区切られているかどうかはわかりません。 XML形式はこれらのことを気にしませんが、awk
またはsed
スクリプトは、考えられるすべてのケース(データのさまざまな可能なエンコーディングを含む)をカバーするために特別な注意が払われない限り、簡単に失敗します。 、その場合、とにかくXMLパーサーを作成する必要があります。
したがって、xmlstarlet
に組み込まれているようなXMLパーサーを使用することは、正しいことです。
次のコマンドは、必要なノードの少なくとも1つがファイルfile.xml
で見つかった場合、入力ファイルのファイル名を出力します。複数のInvoiceLines
ノードが一致した場合、ファイル名は改行を挟んで複数回出力されます。これは、最初から、改行を含むファイル名を失格にすることを意味します。
xmlstarlet sel \
-t -m '/BillingData/InvoiceLinesList/InvoiceLines[chargeType = "D" and charge = "99"]' \
--inp-name -nl file.xml
XPATHクエリは、サブノードInvoiceLines
およびchargeType
を持つすべてのcharge
を指定された値と一致させます。ちなみに、charge
の代わりに@charge
を使用すると、charge
ノードのInvoiceLines
attributeに対してテストされます。
これを単一のディレクトリ内のすべてのXMLファイルに適用します。
xmlstarlet sel \
-t -m '/BillingData/InvoiceLinesList/InvoiceLines[chargeType = "D" and charge = "99"]' \
--inp-name -nl ./*.xml
ファイルが多すぎて上記でエラーが発生した場合は、xargs
を使用できます。
printf '%s\n' ./*.xml | xargs xmlstarlet -t -m ...
または、find
(これはサブディレクトリも検索します):
find . -type f -name '*.xml' -exec xmlstarlet -t -m ... {} +
ファイルリストを一意にしたい場合は、結果をuniq
にパイプします。
次のXMLを使用して上記をテストしました。
<BillingData>
<InvoiceLinesList>
<InvoiceLines>
<chargeType>D</chargeType>
<charge>99</charge>
</InvoiceLines>
<InvoiceLines>
<chargeType>D</chargeType>
<charge>99</charge>
</InvoiceLines>
<InvoiceLines>
<chargeType>E</chargeType>
<charge>99</charge>
</InvoiceLines>
</InvoiceLinesList>
</BillingData>