Odpowiedniki Grep i Sed dla przetwarzania wiersza poleceń XML


147

Podczas wykonywania skryptów powłoki dane zwykle znajdują się w plikach rekordów jednowierszowych, takich jak csv. Obsługa tych danych za pomocą grepi jest naprawdę prosta sed. Ale często mam do czynienia z XML, więc naprawdę chciałbym mieć dostęp do skryptów do tych danych XML za pośrednictwem wiersza poleceń. Jakie są najlepsze narzędzia?


xml_grep nadaje się do greppingu, jak stwierdzono na stackoverflow.com/a/2222224/871134
— Deleplace,

Odpowiedzi:


105

Zauważyłem, że xmlstarlet jest całkiem niezły w tego typu sprawach.

http://xmlstar.sourceforge.net/

Powinien być również dostępny w większości repozytoriów dystrybucji. Wprowadzający poradnik jest tutaj:

http://www.ibm.com/developerworks/library/x-starlet.html


1
Pomyślałem, że na stronie Sourceforge są dostępne pliki binarne systemu Windows.
— Steve Bennett

O ile wiem, nie obsługuje jednak XQuery.
— Steve Bennett

@SteveBennett rzeczywiście tak nie jest, ale funkcje, które dodaje do surowego XPath, są wystarczająco dobre, aby uczynić go konkurencyjnym z „grep and sed”. Jeśli chcesz fantazyjnej, fantazyjnej dobroci XQuery ... cóż, to bardziej przypomina XML odpowiednik perla lub awk. :)
— Charles Duffy

36

Kilka obiecujących narzędzi:

  • nokogiri : parsowanie HTML / XML DOM w Rubim przy użyciu selektorów XPath i CSS

  • hpricot : przestarzałe

  • fxgrep : używa własnej składni podobnej do XPath do wykonywania zapytań dotyczących dokumentów. Napisany w SML, więc instalacja może być trudna.

  • LT XML : XML Toolkit pochodzące z narzędzi SGML, w tym sggrep, sgsort, xmlnormi innych. Używa własnej składni zapytania. Dokumentacja jest bardzo formalna. Napisany w C. LT XML 2 twierdzi, że obsługuje XPath, XInclude i inne standardy W3C.

  • xmlgrep2 : proste i wydajne wyszukiwanie za pomocą XPath. Napisane w Perlu przy użyciu XML :: LibXML i libxml2.

  • XQSharp : obsługuje XQuery, rozszerzenie XPath. Napisany dla .NET Framework.

  • xml-coreutils : zestaw narzędzi Lairda Breyera będący odpowiednikiem GNU coreutils. Omówiono w interesującym eseju na temat tego, co powinien zawierać idealny zestaw narzędzi.

  • xmldiff : Proste narzędzie do porównywania dwóch plików xml.

  • xmltk : nie wydaje się mieć pakietu w debianie, ubuntu, fedorze lub macports, nie ma wydania od 2007 roku i używa nieprzenośnej automatyzacji kompilacji.

xml-coreutils wydaje się najlepiej udokumentowanym i najbardziej zorientowanym na UNIX.


1
Czy nie mógłbyś stworzyć skryptu opakowującego dla programu Ruby i przekazać tablicę argumentów w skrypcie do hpricot? Np. W skrypcie powłoki PHP powinno działać coś takiego: <? Php / path / to / hpricot $ argv?>
— alastairs

25

Do znakomitej listy Josepha Holstena dodaję skrypt wiersza poleceń xpath, który jest dostarczany z biblioteką Perla XML :: XPath. Świetny sposób na wyodrębnienie informacji z plików XML:

 xpath -q -e '/entry[@xml:lang="fr"]' *xml

3
Jest to instalowane domyślnie w osx, ale bez -q -eopcji. Przykład: pobierz wartość atrybutu „pakiet” z węzła „manifest” w „AndroidManifest.xml”:xpath AndroidManifest.xml 'string(/manifest/@package)' 2> /dev/null
— antonj,

25

Jest też xml2i 2xmlpara. Pozwoli to zwykłym narzędziom do edycji ciągów na przetwarzanie XML.

Przykład. q.xml:

<?xml version="1.0"?>
<foo>
    text
    more text
    <textnode>ddd</textnode><textnode a="bv">dsss</textnode>
    <![CDATA[ asfdasdsa <foo> sdfsdfdsf <bar> ]]>
</foo>

xml2 < q.xml

/foo=
/foo=   text
/foo=   more text
/foo=   
/foo/textnode=ddd
/foo/textnode
/foo/textnode/@a=bv
/foo/textnode=dsss
/foo=
/foo=    asfdasdsa <foo> sdfsdfdsf <bar> 
/foo=

xml2 < q.xml | grep textnode | sed 's!/foo!/bar/baz!' | 2xml

<bar><baz><textnode>ddd</textnode><textnode a="bv">dsss</textnode></baz></bar>

PS Są też html2/ 2html.


@Joseph Holsten Yes. Umożliwia hakowanie przy użyciu XML bez zastanawiania się nad XPath.
— Vi.

Miły! Skupiałem się na narzędziach, które nie używają formatu pośredniego, ale idea wiernej, zorientowanej liniowo reprezentacji XML wydaje się świetnym sposobem na dalsze używanie prawdziwych grep i sed. Czy próbowałeś Pyxie? Jak to porównać? Jakieś inne reprezentacje zorientowane na linię? Czy uznałbyś to za lepsze niż zwykłe zastąpienie nowych wierszy xml encją (& # 10;)? Umożliwiłoby to umieszczenie rekordów przynajmniej w tej samej linii. Aha, czy mógłbyś edytować swój post, aby zawierał link do projektu?
— Joseph Holsten

@Joseph Holsten Nie, nie sądzę, aby format pyxie był bardziej przydatny niż format xml2. xml2 zapewnia „pełną ścieżkę” w zagnieżdżonych elementach XML, więc umożliwia bardziej liniowe dopasowywanie i podstawianie. 2xmlMoże również łatwo odtworzyć XML z częściowych (przefiltrowanych) xml2danych wyjściowych.
— Vi.

5
+1 Nie mogę tego wystarczająco zagłosować ... cat foo.xml | xml2 | grep /bar | 2xml- daje taką samą strukturę jak oryginał, ale wszystkie elementy zostały usunięte z wyjątkiem elementów "bar". Niesamowite.
— mogsie

14

Możesz użyć xmllint:

xmllint --xpath //title books.xml

Powinien być dołączony do większości dystrybucji, a także w pakiecie z Cygwin.

$ xmllint --version
xmllint: using libxml version 20900

Widzieć:

$ xmllint
Usage : xmllint [options] XMLfiles ...
        Parse the XML files and output the result of the parsing
        --version : display the version of the XML library used
        --debug : dump a debug tree of the in-memory document
        ...
        --schematron schema : do validation against a schematron
        --sax1: use the old SAX1 interfaces for processing
        --sax: do not build a tree but work just at the SAX level
        --oldxml10: use XML-1.0 parsing rules before the 5th edition
        --xpath expr: evaluate the XPath expression, inply --noout

2
Nie ma --xpathargumentu za xmllint: manpagez.com/man/1/xmllint
— Miserable Variable

1
@MiserableVariable: Strona podręcznika jest nieprawidłowa. Właśnie przejrzałem stronę podręcznika dla mojej wersji: argument xpath nie jest wymieniony. To jest błąd dokumentacji. Zamiast tego spróbuj uruchomić program.
— Dave Jarvis

2
@MiserableVariable --xpathto całkiem nowy dodatek i np. Nie w wersjach RHEL 6 xmllint.
— Daniel Beck

2
Mówiąc dokładniej, xmllint --xpathzostał wprowadzony w libxml2 2.7.7 (w 2010 roku).
— marbu

9

Jeśli szukasz rozwiązania w systemie Windows, Powershell ma wbudowaną funkcję odczytu i zapisu XML.

test.xml:

<root>
  <one>I like applesauce</one>
  <two>You sure bet I do!</two>
</root>

Skrypt PowerShell:

# load XML file into local variable and cast as XML type.
$doc = [xml](Get-Content ./test.xml)

$doc.root.one                                   #echoes "I like applesauce"
$doc.root.one = "Who doesn't like applesauce?"  #replace inner text of <one> node

# create new node...
$newNode = $doc.CreateElement("three")
$newNode.set_InnerText("And don't you forget it!")

# ...and position it in the hierarchy
$doc.root.AppendChild($newNode)

# write results to disk
$doc.save("./testNew.xml")

testNew.xml:

<root>
  <one>Who likes applesauce?</one>
  <two>You sure bet I do!</two>
  <three>And don't you forget it!</three>
</root>

Źródło: /server/26976/update-xml-from-the-command-line-windows


walczył z różnymi narzędziami linuksowymi przez kilka godzin przed skorzystaniem z Powershell. Dziwię się, że to takie trudne - linux cmd-line jest zwykle naprawdę dobry, ale wydaje się, że jest tu dziura. Uwaga: moim przypadkiem użycia było: 1) zlokalizuj węzły według xpath, 2) usuń, jeśli zostaną znalezione, 3) dodaj nowe węzły, 4) zapisz plik. Aktualizowałem kilka konfiguracji solr. Jeśli ktokolwiek zna łatwy / niezawodny sposób na zrobienie tego, to mam wszystkie uszy
— Richard Hauer

Wow, to naprawdę zbliża się do linii akceptowalnego rozwiązania. Ale szczerze mówiąc, prawdopodobnie zaakceptowałbym to, gdyby wyglądało jak xps $doc .root.one xps $doc 'AppendChild("three")'i xps $doc '.three.set_InnerText("And don't you forget it!")', co jest wyraźnie gorsze!
— Joseph Holsten,


6

Zależy dokładnie od tego, co chcesz zrobić.

XSLT może być drogą do zrobienia, ale jest krzywa uczenia się. Wypróbuj xsltproc i pamiętaj, że możesz podać parametry.


4

Jest również saxon-lintz wiersza poleceń z możliwością korzystania z XPath 3.0 / XQuery 3.0. (Inne narzędzia wiersza polecenia używają XPath 1.0).

PRZYKŁADY:

http / html:

$ saxon-lint --html --xpath 'count(//a)' http://stackoverflow.com/q/91791
328

xml:

$ saxon-lint --xpath '//a[@class="x"]' file.xml


3

XQuery może być dobrym rozwiązaniem. Jest (stosunkowo) łatwy do nauczenia i jest standardem W3C.

Polecam XQSharp dla procesora wiersza komend.


1
BaseX ma również procesor XQuery z linii poleceń (oprócz trybu bazy danych) i jest na bieżąco z najnowszymi wersjami standardu (zgodnie z ewoluującą wersją XQuery 3.0 dość dokładnie).
— Charles Duffy


1

Odpowiednik Grepa

Możesz zdefiniować funkcję bash, powiedz „xp” („xpath”), która otacza kod Pythona3. Aby z niego skorzystać, musisz zainstalować python3 i python-lxml. Korzyści:

  1. dopasowanie wyrażeń regularnych, którego brakuje np. xmllint.
  2. Użyj jako filtru (w potoku) w linii poleceń

Jest to łatwe i wydajne w użyciu w następujący sposób:

xmldoc=$(cat <<EOF
<?xml version="1.0" encoding="utf-8"?>
<job xmlns="http://www.sample.com/">programming</job>
EOF
)
selection='//*[namespace-uri()="http://www.sample.com/" and local-name()="job" and re:test(.,"^pro.*ing$")]/text()'
echo "$xmldoc" | xp "$selection"
# prints programming

xp () wygląda mniej więcej tak:

xp()
{ 
local selection="$1";
local xmldoc;
if ! [[ -t 0 ]]; then
    read -rd '' xmldoc;
else
    xmldoc="$2";
fi;
python3 <(printf '%b' "from lxml.html import tostring\nfrom lxml import etree\nfrom sys import stdin\nregexpNS = \"http://exslt.org/regular-expressions\"\ntree = etree.parse(stdin)\nfor e in tree.xpath('""$selection""', namespaces={'re':regexpNS}):\n  if isinstance(e, str):\n    print(e)\n  else:\n    print(tostring(e).decode('UTF-8'))") <<< "$xmldoc"
}

Sed Equivalent

Rozważ użycie xq, które daje ci pełną moc "języka programowania" jq. Jeśli masz zainstalowany python-pip, możesz zainstalować xq za pomocą pip install yq , a następnie w poniższym przykładzie zamieniamy „Keep Accounts” na „Keep Accounts 2”:

xmldoc=$(cat <<'EOF'
<resources>
    <string name="app_name">Keep Accounts</string>
    <string name="login">"login"</string>
    <string name="login_password">"password:"</string>
    <string name="login_account_hint">input to login</string>
    <string name="login_password_hint">input your password</string>
    <string name="login_fail">login failed</string>
</resources>
EOF
)
echo "$xmldoc" | xq '.resources.string = ([.resources.string[]|select(."#text" == "Keep Accounts") ."#text" = "Keep Accounts 2"])' -x

-1

JEdit posiada wtyczkę o nazwie „XQuery”, która zapewnia funkcjonalność zapytań o dokumenty XML.

Niezupełnie wiersz poleceń, ale działa!


Chociaż JEdit prawdopodobnie ma sposób przeszukiwania pliku, nie czyni go to konkurentem grep(1).
— Joseph Holsten,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.