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?

Joseph Holsten
źródło
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

Russ
źródło
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.

Joseph Holsten
źródło
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
bortzmeyer
źródło
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.

Vi.
źródło
@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
Dave Jarvis
źródło
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

Glina
źródło
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.

Adrian Mouat
źródło
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
Gilles Quenot
źródło
4

D. Bohdan utrzymuje repozytorium GitHub o otwartym kodzie źródłowym, które przechowuje listę narzędzi wiersza poleceń dla narzędzi tekstu strukturalnego, jest tam sekcja dotycząca narzędzi XML / HTML:

https://github.com/dbohdan/structured-text-tools#xml-html

Devy
źródło
3

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

Polecam XQSharp dla procesora wiersza komend.

Oliver Hallam
źródło
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
3

Najpierw użyłem xmlstarlet i nadal go używam. Gdy kwerenda pobiera trudne, muszę XML xpath2 i xquery funkcji wsparcia zwracam się do xidel http://www.videlibri.de/xidel.html

trueadjustr
źródło
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
methuselah-0
źródło
-1

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

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

Ben
źródło
Chociaż JEdit prawdopodobnie ma sposób przeszukiwania pliku, nie czyni go to konkurentem grep(1).
Joseph Holsten,