BashでXMLを解析する方法は?


回答:


153

これは実際にはユゼムの答えの単なる説明ですが、私はこれほど多くの編集が誰かに行われるべきだとは思いませんでした、そしてコメントはフォーマットを許可しないので...

rdom () { local IFS=\> ; read -d \< E C ;}

それを「rdom」の代わりに「read_dom」と呼び、少しスペースを空けて、より長い変数を使用します。

read_dom () {
    local IFS=\>
    read -d \< ENTITY CONTENT
}

それで、read_domという関数を定義します。1行目は、IFS(入力フィールド区切り文字)をこの関数に対してローカルにし、>に変更します。つまり、スペース、タブ、または改行で自動的に分割されるのではなく、データを読み取ると、「>」で分割されます。次の行は、標準入力から入力を読み取るように指示し、改行で停止する代わりに、「<」文字(デリミネーターフラグの-d)が表示されたときに停止します。読み取られたものは、IFSを使用して分割され、変数ENTITYおよびCONTENTに割り当てられます。したがって、次のことを考えてください。

<tag>value</tag>

read_dom空の文字列を取得するための最初の呼び出し( '<'が最初の文字であるため)。「>」文字がないため、IFSによって ''に分割されます。次に、両方の変数に空の文字列を割り当てます。2番目の呼び出しは、文字列 'tag> value'を取得します。次に、IFSによって2つのフィールド「タグ」と「値」に分割されます。読むには、その後のような変数を割り当てますENTITY=tagCONTENT=value。3番目の呼び出しは、文字列「/ tag>」を取得します。これは、IFSによって2つのフィールド '/ tag'と ''に分割されます。読むには、その後のような変数を割り当てますENTITY=/tagCONTENT=。ファイルの終わりに達したため、4番目の呼び出しはゼロ以外のステータスを返します。

これで、彼のwhileループが上記に一致するように少しクリーンアップされました。

while read_dom; do
    if [[ $ENTITY = "title" ]]; then
        echo $CONTENT
        exit
    fi
done < xhtmlfile.xhtml > titleOfXHTMLPage.txt

最初の行は、「read_dom関数がゼロのステータスを返す間、次のことを行います」とだけ言っています。2行目では、今見たエンティティが「タイトル」であるかどうかを確認しています。次の行はタグの内容をエコーし​​ます。4つの行が終了します。タイトルエンティティでない場合、ループは6行目で繰り返されます。「xhtmlfile.xhtml」を標準入力(read_dom関数の場合)にリダイレクトし、標準出力を「titleOfXHTMLPage.txt」(ループの前半からのエコー)にリダイレクトします。

次に、(S3にバケットをリストすることで得られるものと同様に)以下を指定しますinput.xml

<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
  <Name>sth-items</Name>
  <IsTruncated>false</IsTruncated>
  <Contents>
    <Key>item-apple-iso@2x.png</Key>
    <LastModified>2011-07-25T22:23:04.000Z</LastModified>
    <ETag>&quot;0032a28286680abee71aed5d059c6a09&quot;</ETag>
    <Size>1785</Size>
    <StorageClass>STANDARD</StorageClass>
  </Contents>
</ListBucketResult>

そして次のループ:

while read_dom; do
    echo "$ENTITY => $CONTENT"
done < input.xml

あなたは得るべきです:

 => 
ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/" => 
Name => sth-items
/Name => 
IsTruncated => false
/IsTruncated => 
Contents => 
Key => item-apple-iso@2x.png
/Key => 
LastModified => 2011-07-25T22:23:04.000Z
/LastModified => 
ETag => &quot;0032a28286680abee71aed5d059c6a09&quot;
/ETag => 
Size => 1785
/Size => 
StorageClass => STANDARD
/StorageClass => 
/Contents => 

したがって、whileYuzemのようなループを作成した場合:

while read_dom; do
    if [[ $ENTITY = "Key" ]] ; then
        echo $CONTENT
    fi
done < input.xml

S3バケット内のすべてのファイルのリストを取得します。

編集 何らかの理由でlocal IFS=\>うまくいかず、グローバルに設定する場合は、次のように関数の最後でリセットする必要があります。

read_dom () {
    ORIGINAL_IFS=$IFS
    IFS=\>
    read -d \< ENTITY CONTENT
    IFS=$ORIGINAL_IFS
}

そうしないと、スクリプトの後半で行を分割すると、混乱します。

編集2 属性の名前と値のペアを分割するには、次のread_dom()ように拡張できます。

read_dom () {
    local IFS=\>
    read -d \< ENTITY CONTENT
    local ret=$?
    TAG_NAME=${ENTITY%% *}
    ATTRIBUTES=${ENTITY#* }
    return $ret
}

次に、次のように必要なデータを解析して取得する関数を記述します。

parse_dom () {
    if [[ $TAG_NAME = "foo" ]] ; then
        eval local $ATTRIBUTES
        echo "foo size is: $size"
    elif [[ $TAG_NAME = "bar" ]] ; then
        eval local $ATTRIBUTES
        echo "bar type is: $type"
    fi
}

その後、あなたがread_dom呼び出す間parse_dom

while read_dom; do
    parse_dom
done

次に、次のマークアップの例を示します。

<example>
  <bar size="bar_size" type="metal">bars content</bar>
  <foo size="1789" type="unknown">foos content</foo>
</example>

次の出力が表示されます。

$ cat example.xml | ./bash_xml.sh 
bar type is: metal
foo size is: 1789

EDIT 3別のユーザーは、FreeBSDで問題が発生していると述べ、次のように、読み取りからの終了ステータスを保存し、read_domの最後にそれを返すことを提案しました。

read_dom () {
    local IFS=\>
    read -d \< ENTITY CONTENT
    local RET=$?
    TAG_NAME=${ENTITY%% *}
    ATTRIBUTES=${ENTITY#* }
    return $RET
}

それがうまくいかない理由は見当たらない


2
IFS(入力フィールド区切り記号)をグローバルにした場合は、最後に元の値にリセットする必要があります。そのため、回答を編集しました。そうしないと、スクリプトで後で行う他の入力分割が台無しになります。ローカルが機能しない理由は、互換モードでbashを使用しているため(shbangが#!/ bin / shのように)、またはbashの古いバージョンであると考えられます。
チャド

30
独自のパーサーを作成できるからといって、そうする必要はありません。
Stephen Niedzielski 2013

1
@chadそれは確かに私も『バッシュXML』への答えを探していたことをAWS」ワークフロー/実装について何か言うwgetの S3バケットの内容を!
Alastair 2013

2
@Alastairは、S3オブジェクトの操作に使用する一連のbashスクリプトについて、github.com / chad3814 / s3scriptsを参照してください– 2013
チャド

5
ローカル変数でのIFSの割り当ては脆弱であり、必要ありません。次のようにしてください:IFS=\< read ...、これはIFSを読み取り呼び出しに対してのみ設定します。(私がreadxmlを解析するためにを使用する慣行を決して支持していないことに注意してください。そうすることは危険を伴うため、避けられるべきであると信じています。)
William Pursell '27

64

bashのみを使用すると、非常に簡単にそれを行うことができます。この関数を追加するだけです。

rdom () { local IFS=\> ; read -d \< E C ;}

これで、rdomをreadのように使用できますが、htmlドキュメントに使用できます。呼び出されると、rdomは要素を変数Eに、内容を変数Cに割り当てます。

たとえば、やりたいことを行うには:

while rdom; do
    if [[ $E = title ]]; then
        echo $C
        exit
    fi
done < xhtmlfile.xhtml > titleOfXHTMLPage.txt

これについて詳しく説明してもらえますか?私はそれがあなたに完全に明確であることを賭けます..そしてこれは素晴らしい答えになるかもしれません-もしあなたがそこで何をしていたかを私に伝えることができれば..それをもう少し分解して、おそらくいくつかのサンプル出力を生成できますか?
Alex Grey

1
オリジナルへの信念-このワンライナーはとても奇抜でエレガントで素晴らしいです。
異端者

1
素晴らしいハックですが、シェルの展開を防ぎ、終了行を正しく解釈するには、エコー "$ C"のような二重引用符を使用する必要がありました(encondingによって異なります)
user311174

8
grepとawkを使用してXMLを解析することはできません。XMLが十分に単純で、時間があまりない場合は、許容できる妥協案かもしれませんが、これは良いソリューションとは言えません。
peterh-モニカを2018年

59

シェルスクリプトから呼び出すことができるコマンドラインツールは次のとおりです。

  • 4xpath -Pythonの4Suiteパッケージのコマンドラインラッパー
  • XMLStarlet
  • xpath-PerlのXPathライブラリのコマンドラインラッパー
  • Xidel-ファイルだけでなくURLでも機能します。JSONでも動作します

また、コマンドラインまたはシェルスクリプトでXML処理を行うために、xmllintとxsltprocを小さなXSL変換スクリプトで使用しています。


2
「xpath」または「4xpath」はどこからダウンロードできますか?
Opher、2011

3
はい、2回目の投票/リクエスト-これらのツールをダウンロードする場所、または手動でラッパーを作成する必要があるという意味ですか?必要がない限り、そうすることに時間を無駄にしたくない。
デビッド

4
sudo apt-get install libxml-xpath-perl
Andrew Wagner

22

xpathユーティリティを使用できます。これは、Perl XML-XPathパッケージとともにインストールされます。

使用法:

/usr/bin/xpath [filename] query

またはXMLStarlet。opensuseにインストールするには、次のコマンドを使用します。

sudo zypper install xmlstarlet

またはcnf xml他のプラットフォームで試してください。


5
(他の回答で提案されているように)独自のシリアライザーを作成するよりも、xmlスターレットを使用する方が間違いなく優れたオプションです。
ブルーノフォンパリ

多くのシステムでは、xpathプリインストールされているはスクリプトのコンポーネントとしての使用には適していません。詳細については、たとえば、stackoverflow.com / questions / 15461737 /…を参照してください。
Tripleee

2
Ubuntu / Debianの場合apt-get install xmlstarlet
rubo77



5

チャドの答えから始めて、コメントを適切に処理して、UMLを解析するための2つの小さな関数(2つ以上のbuですべてを混在させることができます)を備えたCOMPLETE作業ソリューションを次に示します。chadのものはまったく機能しなかったとは言えませんが、フォーマットが正しくないXMLファイルに関しては多くの問題がありました:コメントとスペース/ CR / TAB /などの誤配置を処理するには、もう少しトリッキーでなければなりません。

この回答の目的は、perl、pythonなどを使用した複雑なツールを使用せずに、UMLを解析する必要があるすべての人に、すぐに使用できるbash関数を提供することです。私に関しては、cpanをインストールできず、作業中の古い本番OSのperlモジュールもインストールできず、pythonを使用できません。

まず、この投稿で使用されているUML単語の定義:

<!-- comment... -->
<tag attribute="value">content...</tag>

編集:以下のハンドルで更新された関数:

  • Websphere xml(xmiおよびxmlns属性)
  • 256色の互換性のある端末が必要
  • グレーの24色
  • IBM AIX bash 3.2.16(1)に互換性が追加されました

関数は、最初にxml_readによって再帰的に呼び出されるxml_read_domです。

xml_read_dom() {
# /programming/893585/how-to-parse-xml-in-bash
local ENTITY IFS=\>
if $ITSACOMMENT; then
  read -d \< COMMENTS
  COMMENTS="$(rtrim "${COMMENTS}")"
  return 0
else
  read -d \< ENTITY CONTENT
  CR=$?
  [ "x${ENTITY:0:1}x" == "x/x" ] && return 0
  TAG_NAME=${ENTITY%%[[:space:]]*}
  [ "x${TAG_NAME}x" == "x?xmlx" ] && TAG_NAME=xml
  TAG_NAME=${TAG_NAME%%:*}
  ATTRIBUTES=${ENTITY#*[[:space:]]}
  ATTRIBUTES="${ATTRIBUTES//xmi:/}"
  ATTRIBUTES="${ATTRIBUTES//xmlns:/}"
fi

# when comments sticks to !-- :
[ "x${TAG_NAME:0:3}x" == "x!--x" ] && COMMENTS="${TAG_NAME:3} ${ATTRIBUTES}" && ITSACOMMENT=true && return 0

# http://tldp.org/LDP/abs/html/string-manipulation.html
# INFO: oh wait it doesn't work on IBM AIX bash 3.2.16(1):
# [ "x${ATTRIBUTES:(-1):1}x" == "x/x" -o "x${ATTRIBUTES:(-1):1}x" == "x?x" ] && ATTRIBUTES="${ATTRIBUTES:0:(-1)}"
[ "x${ATTRIBUTES:${#ATTRIBUTES} -1:1}x" == "x/x" -o "x${ATTRIBUTES:${#ATTRIBUTES} -1:1}x" == "x?x" ] && ATTRIBUTES="${ATTRIBUTES:0:${#ATTRIBUTES} -1}"
return $CR
}

そして2番目のもの:

xml_read() {
# /programming/893585/how-to-parse-xml-in-bash
ITSACOMMENT=false
local MULTIPLE_ATTR LIGHT FORCE_PRINT XAPPLY XCOMMAND XATTRIBUTE GETCONTENT fileXml tag attributes attribute tag2print TAGPRINTED attribute2print XAPPLIED_COLOR PROSTPROCESS USAGE
local TMP LOG LOGG
LIGHT=false
FORCE_PRINT=false
XAPPLY=false
MULTIPLE_ATTR=false
XAPPLIED_COLOR=g
TAGPRINTED=false
GETCONTENT=false
PROSTPROCESS=cat
Debug=${Debug:-false}
TMP=/tmp/xml_read.$RANDOM
USAGE="${C}${FUNCNAME}${c} [-cdlp] [-x command <-a attribute>] <file.xml> [tag | \"any\"] [attributes .. | \"content\"]
${nn[2]}  -c = NOCOLOR${END}
${nn[2]}  -d = Debug${END}
${nn[2]}  -l = LIGHT (no \"attribute=\" printed)${END}
${nn[2]}  -p = FORCE PRINT (when no attributes given)${END}
${nn[2]}  -x = apply a command on an attribute and print the result instead of the former value, in green color${END}
${nn[1]}  (no attribute given will load their values into your shell; use '-p' to print them as well)${END}"

! (($#)) && echo2 "$USAGE" && return 99
(( $# < 2 )) && ERROR nbaram 2 0 && return 99
# getopts:
while getopts :cdlpx:a: _OPT 2>/dev/null
do
{
  case ${_OPT} in
    c) PROSTPROCESS="${DECOLORIZE}" ;;
    d) local Debug=true ;;
    l) LIGHT=true; XAPPLIED_COLOR=END ;;
    p) FORCE_PRINT=true ;;
    x) XAPPLY=true; XCOMMAND="${OPTARG}" ;;
    a) XATTRIBUTE="${OPTARG}" ;;
    *) _NOARGS="${_NOARGS}${_NOARGS+, }-${OPTARG}" ;;
  esac
}
done
shift $((OPTIND - 1))
unset _OPT OPTARG OPTIND
[ "X${_NOARGS}" != "X" ] && ERROR param "${_NOARGS}" 0

fileXml=$1
tag=$2
(( $# > 2 )) && shift 2 && attributes=$*
(( $# > 1 )) && MULTIPLE_ATTR=true

[ -d "${fileXml}" -o ! -s "${fileXml}" ] && ERROR empty "${fileXml}" 0 && return 1
$XAPPLY && $MULTIPLE_ATTR && [ -z "${XATTRIBUTE}" ] && ERROR param "-x command " 0 && return 2
# nb attributes == 1 because $MULTIPLE_ATTR is false
[ "${attributes}" == "content" ] && GETCONTENT=true

while xml_read_dom; do
  # (( CR != 0 )) && break
  (( PIPESTATUS[1] != 0 )) && break

  if $ITSACOMMENT; then
    # oh wait it doesn't work on IBM AIX bash 3.2.16(1):
    # if [ "x${COMMENTS:(-2):2}x" == "x--x" ]; then COMMENTS="${COMMENTS:0:(-2)}" && ITSACOMMENT=false
    # elif [ "x${COMMENTS:(-3):3}x" == "x-->x" ]; then COMMENTS="${COMMENTS:0:(-3)}" && ITSACOMMENT=false
    if [ "x${COMMENTS:${#COMMENTS} - 2:2}x" == "x--x" ]; then COMMENTS="${COMMENTS:0:${#COMMENTS} - 2}" && ITSACOMMENT=false
    elif [ "x${COMMENTS:${#COMMENTS} - 3:3}x" == "x-->x" ]; then COMMENTS="${COMMENTS:0:${#COMMENTS} - 3}" && ITSACOMMENT=false
    fi
    $Debug && echo2 "${N}${COMMENTS}${END}"
  elif test "${TAG_NAME}"; then
    if [ "x${TAG_NAME}x" == "x${tag}x" -o "x${tag}x" == "xanyx" ]; then
      if $GETCONTENT; then
        CONTENT="$(trim "${CONTENT}")"
        test ${CONTENT} && echo "${CONTENT}"
      else
        # eval local $ATTRIBUTES => eval test "\"\$${attribute}\"" will be true for matching attributes
        eval local $ATTRIBUTES
        $Debug && (echo2 "${m}${TAG_NAME}: ${M}$ATTRIBUTES${END}"; test ${CONTENT} && echo2 "${m}CONTENT=${M}$CONTENT${END}")
        if test "${attributes}"; then
          if $MULTIPLE_ATTR; then
            # we don't print "tag: attr=x ..." for a tag passed as argument: it's usefull only for "any" tags so then we print the matching tags found
            ! $LIGHT && [ "x${tag}x" == "xanyx" ] && tag2print="${g6}${TAG_NAME}: "
            for attribute in ${attributes}; do
              ! $LIGHT && attribute2print="${g10}${attribute}${g6}=${g14}"
              if eval test "\"\$${attribute}\""; then
                test "${tag2print}" && ${print} "${tag2print}"
                TAGPRINTED=true; unset tag2print
                if [ "$XAPPLY" == "true" -a "${attribute}" == "${XATTRIBUTE}" ]; then
                  eval ${print} "%s%s\ " "\${attribute2print}" "\${${XAPPLIED_COLOR}}\"\$(\$XCOMMAND \$${attribute})\"\${END}" && eval unset ${attribute}
                else
                  eval ${print} "%s%s\ " "\${attribute2print}" "\"\$${attribute}\"" && eval unset ${attribute}
                fi
              fi
            done
            # this trick prints a CR only if attributes have been printed durint the loop:
            $TAGPRINTED && ${print} "\n" && TAGPRINTED=false
          else
            if eval test "\"\$${attributes}\""; then
              if $XAPPLY; then
                eval echo "\${g}\$(\$XCOMMAND \$${attributes})" && eval unset ${attributes}
              else
                eval echo "\$${attributes}" && eval unset ${attributes}
              fi
            fi
          fi
        else
          echo eval $ATTRIBUTES >>$TMP
        fi
      fi
    fi
  fi
  unset CR TAG_NAME ATTRIBUTES CONTENT COMMENTS
done < "${fileXml}" | ${PROSTPROCESS}
# http://mywiki.wooledge.org/BashFAQ/024
# INFO: I set variables in a "while loop" that's in a pipeline. Why do they disappear? workaround:
if [ -s "$TMP" ]; then
  $FORCE_PRINT && ! $LIGHT && cat $TMP
  # $FORCE_PRINT && $LIGHT && perl -pe 's/[[:space:]].*?=/ /g' $TMP
  $FORCE_PRINT && $LIGHT && sed -r 's/[^\"]*([\"][^\"]*[\"][,]?)[^\"]*/\1 /g' $TMP
  . $TMP
  rm -f $TMP
fi
unset ITSACOMMENT
}

そして最後に、rtrim、trim、echo2(stderrへ)関数:

rtrim() {
local var=$@
var="${var%"${var##*[![:space:]]}"}"   # remove trailing whitespace characters
echo -n "$var"
}
trim() {
local var=$@
var="${var#"${var%%[![:space:]]*}"}"   # remove leading whitespace characters
var="${var%"${var##*[![:space:]]}"}"   # remove trailing whitespace characters
echo -n "$var"
}
echo2() { echo -e "$@" 1>&2; }

色付け:

ああ、あなたは最初にいくつかのきちんとした色付け動的変数を定義してエクスポートする必要もあります:

set -a
TERM=xterm-256color
case ${UNAME} in
AIX|SunOS)
  M=$(${print} '\033[1;35m')
  m=$(${print} '\033[0;35m')
  END=$(${print} '\033[0m')
;;
*)
  m=$(tput setaf 5)
  M=$(tput setaf 13)
  # END=$(tput sgr0)          # issue on Linux: it can produces ^[(B instead of ^[[0m, more likely when using screenrc
  END=$(${print} '\033[0m')
;;
esac
# 24 shades of grey:
for i in $(seq 0 23); do eval g$i="$(${print} \"\\033\[38\;5\;$((232 + i))m\")" ; done
# another way of having an array of 5 shades of grey:
declare -a colorNums=(238 240 243 248 254)
for num in 0 1 2 3 4; do nn[$num]=$(${print} "\033[38;5;${colorNums[$num]}m"); NN[$num]=$(${print} "\033[48;5;${colorNums[$num]}m"); done
# piped decolorization:
DECOLORIZE='eval sed "s,${END}\[[0-9;]*[m|K],,g"'

すべてのものをロードする方法:

関数を作成し、FPATH(ksh)またはFPATHのエミュレーション(bash)を介してそれらをロードする方法を知っている

そうでない場合は、すべてをコマンドラインにコピーして貼り付けます。

それはどのように機能しますか:

xml_read [-cdlp] [-x command <-a attribute>] <file.xml> [tag | "any"] [attributes .. | "content"]
  -c = NOCOLOR
  -d = Debug
  -l = LIGHT (no \"attribute=\" printed)
  -p = FORCE PRINT (when no attributes given)
  -x = apply a command on an attribute and print the result instead of the former value, in green color
  (no attribute given will load their values into your shell as $ATTRIBUTE=value; use '-p' to print them as well)

xml_read server.xml title content     # print content between <title></title>
xml_read server.xml Connector port    # print all port values from Connector tags
xml_read server.xml any port          # print all port values from any tags

デバッグモード(-d)では、コメントと解析された属性がstderrに出力されます


上記の2つの関数を使用して、以下を生成しようとしています./read_xml.sh: line 22: (-1): substring expression < 0
khmarbaise 2014年

行22:[ "x${ATTRIBUTES:(-1):1}x" == "x?x" ] ...
khmarbaise 2014年

申し訳ありません、khmarbaise、これらはbashシェル関数です。それらをシェルスクリプトとして適応させる場合は、いくつかの小さな適応を期待する必要があります。また、更新された関数はエラーを処理します;)
スカベンジャー

4

純粋なシェルXML解析ツールについては知りません。したがって、おそらく他の言語で書かれたツールが必要になります。

私のXML :: Twig Perlモジュールには、このようなツールが付属しています。xml_grepおそらく、希望するとおりに記述しますxml_grep -t '/html/head/title' xhtmlfile.xhtml > titleOfXHTMLPage.txt(この-tオプションでは、結果がxmlではなくテキストとして表示されます)。


4

別のコマンドラインツールは私の新しいXidelですです。また、前述のxpath / xmlstarletとは異なり、XPath 2およびXQueryもサポートしています。

タイトルは次のように読むことができます:

xidel xhtmlfile.xhtml -e /html/head/title > titleOfXHTMLPage.txt

また、複数の変数をbashにエクスポートする優れた機能もあります。例えば

eval $(xidel xhtmlfile.xhtml -e 'title := //title, imgcount := count(//img)' --output-format bash )

$titleタイトルと$imgcountファイル内の画像の数を設定します。これは、bashで直接解析するのと同じくらい柔軟でなければなりません。


これはまさに私が必要としたものです!:)
Thomas Daugaard 2013年

2

まあ、あなたはxpathユーティリティを使うことができます。perlのXML :: Xpathに含まれていると思います。


2

LinuxとWindowsのフォーマット間でXMLファイルのファイルパスを変換するための調査を行った結果、興味深いチュートリアルと解決策が見つかりました。


2

必要な機能を実行できる既製のコンソールユーティリティはかなりありますが、Pythonなどの汎用プログラミング言語で数行のコードを書くと、簡単に拡張して適応できるので、時間がかかりません。あなたの要望。

以下は、lxml解析に使用するpythonスクリプトです。ファイルの名前またはURLを最初のパラメーターとして、XPath式を2番目のパラメーターとして受け取り、指定された式に一致する文字列/ノードを出力します。

例1

#!/usr/bin/env python
import sys
from lxml import etree

tree = etree.parse(sys.argv[1])
xpath_expression = sys.argv[2]

#  a hack allowing to access the
#  default namespace (if defined) via the 'p:' prefix    
#  E.g. given a default namespaces such as 'xmlns="http://maven.apache.org/POM/4.0.0"'
#  an XPath of '//p:module' will return all the 'module' nodes
ns = tree.getroot().nsmap
if ns.keys() and None in ns:
    ns['p'] = ns.pop(None)
#   end of hack    

for e in tree.xpath(xpath_expression, namespaces=ns):
    if isinstance(e, str):
        print(e)
    else:
        print(e.text and e.text.strip() or etree.tostring(e, pretty_print=True))

lxmlでインストールできますpip install lxml。ubuntuではを使用できますsudo apt install python-lxml

使用法

python xpath.py myfile.xml "//mynode"

lxml また、入力としてURLを受け入れます。

python xpath.py http://www.feedforall.com/sample.xml "//link"

:XMLに接頭辞のないデフォルトの名前空間(例:)がある場合、式xmlns=http://abc...からp接頭辞(「hack」によって提供される)を使用する必要が//p:moduleありpom.xmlます。たとえば、ファイルからモジュールを取得する場合などです。pプレフィックスが既にXMLにマッピングされている場合は、別のプレフィックスを使用するようにスクリプトを変更する必要があります。


例2

Apache Mavenファイルからモジュール名を抽出するという狭い目的に役立つ1回限りのスクリプト。ノード名(module)の前にデフォルトの名前空間が付いていることに注意してください{http://maven.apache.org/POM/4.0.0}

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modules>
        <module>cherries</module>
        <module>bananas</module>
        <module>pears</module>
    </modules>
</project>

module_extractor.py

from lxml import etree
for _, e in etree.iterparse(open("pom.xml"), tag="{http://maven.apache.org/POM/4.0.0}module"):
    print(e.text)

これは、追加のパッケージのインストールを避けたい場合、またはアクセスできない場合に最適です。ビルドマシンでは、私が余分に正当化することができますpip installオーバーapt-getまたはyumコールを。ありがとう!
E.モファット2018年

0

ユゼムの方法は、関数と変数の割り当ての<and >記号の順序を逆にすることで改善でき、次のrdomようになります。

rdom () { local IFS=\> ; read -d \< E C ;}

になる:

rdom () { local IFS=\< ; read -d \> C E ;}

このように解析が行われない場合、XMLファイルの最後のタグに到達することはありません。whileループの最後に別のXMLファイルを出力する場合、これは問題になる可能性があります。


0

これは、XML属性が必要な場合に機能します。

$ cat alfa.xml
<video server="asdf.com" stream="H264_400.mp4" cdn="limelight"/>

$ sed 's.[^ ]*..;s./>..' alfa.xml > alfa.sh

$ . ./alfa.sh

$ echo "$stream"
H264_400.mp4

-1

「XML、JSONを解析しないでください...適切なツールなしでbashから」というのは健全なアドバイスですが、私はそうは思いません。これが副業である場合、適切なツールを探してそれを学ぶのはお腹いっぱいです。Awkは数分でそれを行うことができます。私のプログラムは上記のすべての種類のデータを処理する必要があります。地獄、私は30のツールをテストして、問題を数分で理解できれば、必要な5-7-10の異なる形式を解析したくありません。XMLやJSONなどは気にしません!それらすべてに対して単一のソリューションが必要です。

例として、私のSmartHomeプログラムは家を実行します。それをしている間、それは私が制御できない非常に多くの異なるフォーマットで大量のデータを読みます。必要なデータの読み取りに数分以上費やしたくないので、専用の適切なツールを使用することはありません。FSとRSの調整により、このawkソリューションはあらゆるテキスト形式で完全に機能します。しかし、主なタスクが主にその形式のデータのロードで作業することである場合、それは適切な答えではない場合があります!

昨日直面したbashからXMLを解析する問題。階層データ形式でこれを行う方法を次に示します。おまけとして、bashスクリプトの変数にデータを直接割り当てます。

シンを読みやすくするために、解決策を段階的に示します。OPテストデータから、test.xmlファイルを作成しました

XMLをbashで解析し、データを90文字で抽出します。

awk 'BEGIN { FS="<|>"; RS="\n" }; /host|username|password|dbname/ { print $2, $4 }' test.xml

私は通常、より読みやすいバージョンを使用します。なぜなら、私はしばしば別の方法でテストする必要があるので、実際に変更する方が簡単だからです。

awk 'BEGIN { FS="<|>"; RS="\n" }; { if ($0 ~ /host|username|password|dbname/) print $2,$4}' test.xml

形式がどのように呼ばれるかは気にしません。私は最も単純な解決策だけを求めます。この特定のケースでは、改行がレコード区切り(RS)および<>区切りフィールド(FS)であることがデータからわかります。私の元のケースでは、2つのレコード内の6つの値の複雑なインデックス付けを行い、それらを関連付けて、データが存在する場合とフィールド(レコード)が存在する場合と存在しない場合を見つけました。問題を完全に解決するには、4行のawkが必要でした。だから、それを使用する前にそれぞれのニーズにアイデアを適応させてください!

2番目の部分は、必要な文字列が行にある(RS)かどうかを単純に調べ、必要なフィールド(FS)を出力します。上記は、私がこの方法で使用した最後のコマンド(4倍以上)からコピーして適応するのに約30秒かかりました。これで完了です。90文字で行われます。

ただし、スクリプトの変数にデータを常にきれいに入れる必要があります。最初に次のように構成をテストします。

awk 'BEGIN { FS="<|>"; RS="\n" }; { if ($0 ~ /host|username|password|dbname/) print $2"=\""$4"\"" }' test.xml

場合によっては、printの代わりにprintfを使用します。すべてが問題ないように見えたら、変数への値の割り当てを終了します。私は多くの人が「評価」は「悪」だと思っていることを知っています。コメントする必要はありません:) Trickは何年もの間、私の4つのネットワークすべてで完全に機能します。しかし、これが悪い習慣である理由がわからない場合は、学習を続けてください。bash変数の割り当てと十分な間隔を含む私のソリューションでは、すべてを実行するために120文字が必要です。

eval $( awk 'BEGIN { FS="<|>"; RS="\n" }; { if ($0 ~ /host|username|password|dbname/) print $2"=\""$4"\"" }' test.xml ); echo "host: $host, username: $username, password: $password dbname: $dbname"
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.