Bagaimana cara mencetak XML dari baris perintah?

528

Terkait: Bagaimana saya bisa cukup mencetak JSON dalam skrip shell (unix)?

Apakah ada skrip shell (unix) untuk memformat XML dalam bentuk yang dapat dibaca manusia?

Pada dasarnya, saya ingin mengubah yang berikut:

<root><foo a="b">lorem</foo><bar value="ipsum" /></root>

... menjadi sesuatu seperti ini:

<root>
    <foo a="b">lorem</foo>
    <bar value="ipsum" />
</root>
svidgen
sumber
1
Agar xmllinttersedia di sistem Debian, Anda perlu menginstal paket libxml2-utils( libxml2tidak menyediakan alat ini, setidaknya tidak pada Debian 5.0 "Lenny" dan 6.0 "Squeeze").
twonkeys

Jawaban:

909

libxml2-utils

Utilitas ini dilengkapi dengan libxml2-utils:

echo '<root><foo a="b">lorem</foo><bar value="ipsum" /></root>' |
    xmllint --format -

Perl XML::Twig

Perintah ini dilengkapi dengan XML :: Twig modul, terkadang xml-twig-toolspaket:

echo '<root><foo a="b">lorem</foo><bar value="ipsum" /></root>' |
    xml_pp

xmlstarlet

Perintah ini dilengkapi dengan xmlstarlet:

echo '<root><foo a="b">lorem</foo><bar value="ipsum" /></root>' |
    xmlstarlet format --indent-tab

tidy

Periksa tidypaket:

echo '<root><foo a="b">lorem</foo><bar value="ipsum" /></root>' |
    tidy -xml -i -

Python

Python xml.dom.minidomdapat memformat XML (baik python2 dan python3):

echo '<root><foo a="b">lorem</foo><bar value="ipsum" /></root>' |
    python -c 'import sys;import xml.dom.minidom;s=sys.stdin.read();print(xml.dom.minidom.parseString(s).toprettyxml())'

saxon-lint

Anda membutuhkan saxon-lint:

echo '<root><foo a="b">lorem</foo><bar value="ipsum" /></root>' |
    saxon-lint --indent --xpath '/' -

saxon-HE

Anda membutuhkan saxon-HE:

 echo '<root><foo a="b">lorem</foo><bar value="ipsum" /></root>' |
    java -cp /usr/share/java/saxon/saxon9he.jar net.sf.saxon.Query \
    -s:- -qs:/ '!indent=yes'
Gilles Quenot
sumber
Bagus, jawaban cepat. Opsi pertama sepertinya akan lebih banyak ditemukan di instalasi * nix modern. Poin kecil; tetapi bisakah itu dipanggil tanpa bekerja melalui file perantara? Yaitu , echo '<xml .. />' | xmllint --some-read-from-stdn-option?
svidgen
Paketnya ada libxml2-utilsdi ubuntu saya yang cantik.
franzlorenzon
1
Perhatikan bahwa "cat data.xml | xmllint --format - | tee data.xml" tidak berfungsi. Di sistem saya kadang-kadang bekerja untuk file kecil, tetapi selalu terpotong file besar. Jika Anda benar-benar ingin melakukan sesuatu di tempat, baca backreference.org/2011/01/29/in-place-editing-of-files
user1346466
1
Untuk menyelesaikannya UnicodeDecodeError: 'ascii' codec can't decode byte 0xc5 in position 805: ordinal not in range(128)dalam versi python, Anda ingin mendefinisikan PYTHONIOENCODING="UTF-8":cat some.xml | PYTHONIOENCODING="UTF-8" python -c 'import sys;import xml.dom.minidom;s=sys.stdin.read();print xml.dom.minidom.parseString(s).toprettyxml()' > pretty.xml
FelikZ
1
Perhatikan bahwa rapi juga dapat memformat xml tanpa elemen root . Ini berguna untuk memformat melalui pipa, bagian xml (misalnya diekstraksi dari log). echo '<x></x><y></y>' | tidy -xml -iq
Marinos An
157

xmllint --format yourxmlfile.xml

xmllint adalah alat XML baris perintah dan termasuk dalam libxml2( http://xmlsoft.org/ ).

==============================================

Catatan: Jika Anda belum libxml2menginstal Anda dapat menginstalnya dengan melakukan hal berikut:

CentOS

cd /tmp
wget ftp://xmlsoft.org/libxml2/libxml2-2.8.0.tar.gz
tar xzf libxml2-2.8.0.tar.gz
cd libxml2-2.8.0/
./configure
make
sudo make install
cd

Ubuntu

sudo apt-get install libxml2-utils

Cygwin

apt-cyg install libxml2

MacOS

Untuk menginstal ini pada MacOS dengan Homebrew cukup lakukan: brew install libxml2

Git

Juga tersedia di Git jika Anda menginginkan kode: git clone git://git.gnome.org/libxml2

Crmpicco
sumber
4
Jawaban sputnick berisi informasi ini, tetapi jawaban crmpicco adalah jawaban yang paling berguna di sini untuk pertanyaan umum tentang cara mencetak XML yang cantik.
Seth Difley
2
kita dapat menuliskan output xml yang diformat ke beberapa file xml lainnya dan menggunakannya .. misalnya xmllint --format yourxmlfile.xml >> new-file.xml
LearnToLive
2
Pada Ubuntu 16.04 Anda dapat menggunakan yang berikut ini:sudo apt-get install libxml2-utils
Melle
Ini juga berfungsi pada Windows; gituntuk pengunduhan Windows bahkan menginstal versi terbaru dari xmllint. Contoh:"C:\Program Files\Git\usr\bin\xmllint.exe" --format [email protected] > [email protected]
Jeroen Wiert Pluimers
41

Anda juga dapat menggunakan rapi , yang mungkin perlu diinstal terlebih dahulu (misalnya di Ubuntu: sudo apt-get install tidy).

Untuk ini, Anda akan mengeluarkan sesuatu seperti berikut:

tidy -xml -i your-file.xml > output.xml

Catatan: memiliki banyak tanda pembacaan tambahan, tetapi perilaku bungkus kata agak mengganggu untuk diatasi ( http://tidy.sourceforge.net/docs/quickref.html ).

matanster
sumber
1
Bermanfaat, karena saya tidak bisa mendapatkan xmllint untuk menambahkan linebreak ke file xml baris tunggal. Terima kasih!
xlttj
tidybekerja dengan baik untuk saya juga. Tidak seperti hxnormalizeini, tindakan ini benar-benar menutup <body>tag.
Sridhar Sarnobat
9
BTW, berikut adalah beberapa pilihan yang saya telah menemukan berguna: tidy --indent yes --indent-spaces 4 --indent-attributes yes --wrap-attributes yes --input-xml yes --output-xml yes < InFile.xml > OutFile.xml.
Victor Yarema
2
Tip bagus @ Viktorarema. Saya menggabungkannya dengan pygmentize dan menambahkannya ke .bashrc saya: alias prettyxml='tidy --indent yes --indent-spaces 4 --indent-attributes yes --wrap-attributes yes --input-xml yes --output-xml yes | pygmentize -l xml' dan kemudiancurl url | prettyxml
Net Wolf
13

Anda tidak menyebutkan file, jadi saya berasumsi Anda ingin memberikan string XML sebagai input standar pada baris perintah. Dalam hal ini, lakukan hal berikut:

$ echo '<root><foo a="b">lorem</foo><bar value="ipsum" /></root>' | xmllint --format -
David
sumber
12

Tanpa menginstal apa pun di macOS / kebanyakan Unix.

Menggunakan tidy

cat filename.xml | tidy -xml -iq

Mengarahkan melihat file dengan cat untuk merapikan menentukan jenis file xml dan untuk indent sementara output diam akan menekan output kesalahan. JSON juga bekerja dengan -json.

jasonleonhard
sumber
1
Anda tidak perlu catlangkah: tidy -xml -iq filename.xml. Juga, Anda bahkan dapat tidy -xml -iq filename.xmlmenggunakan -mopsi untuk memodifikasi file asli ...
janniks
10

xmllint mendukung pemformatan di tempat :

for f in *.xml; do xmllint -o $f --format $f; done

Seperti yang ditulis Daniel Veillard:

Saya pikir xmllint -o tst.xml --format tst.xml harus aman karena parser akan sepenuhnya memuat input ke dalam pohon sebelum membuka output untuk membuat cerita bersambung.

Level indent dikendalikan oleh XMLLINT_INDENTvariabel lingkungan yang secara default 2 spasi. Contoh cara mengubah indentasi ke 4 spasi:

XMLLINT_INDENT='    '  xmllint -o out.xml --format in.xml

Anda mungkin memiliki kekurangan dengan --recoveropsi ketika dokumen XML Anda rusak. Atau coba parser HTML yang lemah dengan output XML yang ketat:

xmllint --html --xmlout <in.xml >out.xml

--nsclean, --nonet, --nocdata, --noblanksDll mungkin berguna. Baca halaman manual.

apt-get install libxml2-utils
apt-cyg install libxml2
brew install libxml2
gavenkoa
sumber
2

Ini memakan waktu lama untuk menemukan sesuatu yang berfungsi pada mac saya. Inilah yang bekerja untuk saya:

brew install xmlformat
cat unformatted.html | xmlformat
Sridhar Sarnobat
sumber
1
Jawaban saya di atas berfungsi pada mac
jasonleonhard
1

Saya ingin menambahkan solusi Bash murni, karena tidak 'sulit' untuk melakukannya dengan tangan saja, dan kadang-kadang Anda tidak ingin menginstal alat tambahan untuk melakukan pekerjaan itu.

#!/bin/bash

declare -i currentIndent=0
declare -i nextIncrement=0
while read -r line ; do
  currentIndent+=$nextIncrement
  nextIncrement=0
  if [[ "$line" == "</"* ]]; then # line contains a closer, just decrease the indent
    currentIndent+=-1
  else
    dirtyStartTag="${line%%>*}"
    dirtyTagName="${dirtyStartTag%% *}"
    tagName="${dirtyTagName//</}"
    # increase indent unless line contains closing tag or closes itself
    if [[ ! "$line" =~ "</$tagName>" && ! "$line" == *"/>"  ]]; then
      nextIncrement+=1
    fi
  fi

  # print with indent
  printf "%*s%s" $(( $currentIndent * 2 )) # print spaces for the indent count
  echo $line
done <<< "$(cat - | sed 's/></>\n</g')" # separate >< with a newline

Tempel di file skrip, dan pipa di xml. Ini mengasumsikan xml semuanya dalam satu baris, dan tidak ada spasi tambahan di mana pun. Orang bisa dengan mudah menambahkan beberapa tambahan \s*ke regex untuk memperbaikinya.

Leondepeon
sumber