2010-02-08 23 views
31

Tôi đã một file XML với các nội dung:Khai thác dữ liệu từ một tập tin XML đơn giản

<?xml version="1.0" encoding="utf-8"?> 
<job xmlns="http://www.sample.com/">programming</job> 

Tôi cần một cách để trích xuất những gì có trong <job..></job> thẻ, programmin trong trường hợp này. Điều này nên được thực hiện trên dấu nhắc lệnh linux, sử dụng grep/sed/awk.

+0

Nếu tập tin XML của bạn chứa này: Tom & Jerry bạn muốn kết quả có XML thoát bỏ lại một mình: Tom & Jerry hoặc sẽ bạn muốn thoát được hoàn tác, như là một phân tích cú pháp XML sẽ: Tom & Jerry Nếu đó là thứ hai, xin lỗi, tôi không biết làm thế nào để làm điều đó với các công cụ văn bản Unix. –

+0

@Paul 's/&/\ &/g', tương tự cho' " 'vv, tất nhiên nó sẽ không khái quát hóa cho các thực thể do người dùng xác định, v.v. – 13ren

Trả lời

51

Bạn có thực sự phải chỉ sử dụng những công cụ? Họ đang không được thiết kế cho việc xử lý XML, và mặc dù nó có thể để có được một cái gì đó mà làm việc OK hầu hết thời gian, nó sẽ thất bại trên trường hợp cạnh, giống như mã hóa, ngắt dòng, vv

Tôi khuyên bạn nên xml_grep:

xml_grep 'job' jobs.xml --text_only 

Mà cho kết quả:

programming 

trên ubuntu/debian, xml_grep là trong xml-cành-công cụ gói.

+0

Hướng dẫn cài đặt chặt chẽ sẽ là tuyệt vời cho xml_grep –

+0

sudo apt-get cài đặt xml-twig-tools – FredFury

0

Làm thế nào về:

cat a.xml | grep '<job' | cut -d '>' -f 2 | cut -d '<' -f 1 
+3

UUOC. 'grep ' ghostdog74

+0

@ghost * nhưng nhưng, tôi nghĩ rằng nó sạch hơn/đẹp hơn/không quá nhiều lãng phí/quyền sở hữu của tôi để lãng phí quá trình! * Http://partmaps.org/era/unix/award.html#cat (trên thực tế, tôi nghĩ việc chỉnh sửa tên tệp dễ dàng hơn, vì gần hơn bắt đầu) – 13ren

+3

Nếu bạn sử dụng ' Thor

11
grep '<job' file_name | cut -f2 -d">"|cut -f1 -d"<" 
+0

chỉ rằng nó không thành công nếu thẻ nằm trên các dòng riêng biệt – ghostdog74

+7

Có khoảng một chục cách khác mà XML được định dạng tốt có thể làm cho lỗi đó. –

6

chỉ cần sử dụng awk, không cần các công cụ bên ngoài khác. Dưới đây hoạt động nếu các thẻ bạn muốn xuất hiện trong multitine.

$ cat file 
test 
<job xmlns="http://www.sample.com/">programming</job> 
<job xmlns="http://www.sample.com/"> 
programming</job> 

$ awk -vRS="</job>" '{gsub(/.*<job.*>/,"");print}' file 
programming 

programming 
+0

'' hợp lệ, nhưng tập lệnh của bạn không nhận ra. '

+3

Có một số lượng đáng kể các công cụ khác nhau sử dụng ký pháp XPath tiêu chuẩn để trích xuất thông tin từ XML - 'xmlstarlet' chỉ là một. Những người khác bao gồm 'xmllint',' xpath', v.v. Xem http://stackoverflow.com/questions/15461737/how-to-execute-xpath-one-liners-from-shell – tripleee

8

Xin đừng sử dụng dòng và regex dựa trên phân tích cú pháp XML. Đó là một ý tưởng tồi. Bạn có thể có XML giống hệt ngữ nghĩa với định dạng khác nhau, và phân tích cú pháp dựa trên dòng lệnh và regex đơn giản là không thể đối phó với nó.

Những điều như thẻ unary và biến dòng gói - những đoạn 'nói' điều tương tự:

<root> 
    <sometag val1="fish" val2="carrot" val3="narf"></sometag> 
</root> 


<root> 
    <sometag 
     val1="fish" 
     val2="carrot" 
     val3="narf"></sometag> 
</root> 

<root 
><sometag 
val1="fish" 
val2="carrot" 
val3="narf" 
></sometag></root> 

<root><sometag val1="fish" val2="carrot" val3="narf"/></root> 

Hy vọng rằng điều này làm cho nó rõ ràng lý do tại sao thực hiện một phân tích cú pháp regex/dòng dựa là khó khăn? May mắn thay, bạn không cần. Nhiều ngôn ngữ kịch bản có ít nhất một, đôi khi nhiều tùy chọn phân tích cú pháp hơn.

Như một áp phích trước đã ám chỉ đến - xml_grep khả dụng. Đó thực sự là một công cụ dựa trên thư viện perl XML::Twig. Tuy nhiên những gì nó làm là sử dụng 'biểu thức xpath' để tìm một cái gì đó, và phân biệt giữa cấu trúc tài liệu, thuộc tính và 'nội dung'.

Ví dụ::

xml_grep 'job' jobs.xml --text_only 

Tuy nhiên vì lợi ích của làm cho câu trả lời tốt hơn, sau đây là một vài ví dụ về 'roll của riêng bạn dựa trên nguồn dữ liệu của bạn:

cách đầu tiên:

Sử dụng twig handlers mà bắt các yếu tố của một loại cụ thể và hành động trên chúng. Lợi thế của việc làm theo cách này là nó phân tích cú pháp XML 'khi bạn đi', và cho phép bạn sửa đổi nó trong chuyến bay nếu bạn cần. Điều này đặc biệt hữu ích cho loại bỏ XML 'xử lý' khi bạn đang làm việc với các tập tin lớn, sử dụng purge hoặc flush:

#!/usr/bin/perl 

use strict; 
use warnings; 

use XML::Twig; 

XML::Twig->new(
    twig_handlers => { 
     'job' => sub { print $_ ->text } 
    } 
    )->parse(<>); 

nào sẽ sử dụng <> để có đầu vào (đường ống trong, hoặc quy định thông qua dòng lệnh ./myscript somefile.xml) và quá trình nó - mỗi phần tử job, nó sẽ trích xuất và in bất kỳ văn bản nào được liên kết. (Bạn có thể muốn print $_ -> text,"\n" để chèn một linefeed).

Bởi vì nó phù hợp với trên 'công việc' yếu tố, nó cũng sẽ phù hợp trên các yếu tố công việc lồng nhau:

<job>programming 
    <job>anotherjob</job> 
</job> 

sẽ phù hợp với hai lần, nhưng in một số lượng gấp đôi quá. Tuy nhiên, bạn có thể khớp với số /job nếu muốn. Rất hữu ích - điều này cho phép bạn, ví dụ: in và xóa một phần tử hoặc sao chép và dán một phần thay đổi cấu trúc XML.

Ngoài - phân tích đầu tiên, và 'in' dựa trên cấu trúc:

my $twig = XML::Twig->new()->parse(<>); 
print $twig -> root -> text; 

Như job là phần tử gốc của bạn, tất cả chúng ta cần làm là in nội dung của nó.

Nhưng chúng ta có thể sáng suốt hơn một chút, và tìm kiếm job hoặc /job và in mà cụ thể thay vì:

my $twig = XML::Twig->new()->parse(<>); 
print $twig -> findnodes('/job',0)->text; 

Bạn có thể sử dụng XML::Twig s pretty_print tùy chọn để định dạng lại XML của bạn quá:

XML::Twig->new('pretty_print' => 'indented_a')->parse(<>) -> print; 

Có nhiều tùy chọn định dạng đầu ra khác nhau, nhưng đối với XML đơn giản hơn (giống như của bạn) hầu hết sẽ trông khá giống nhau.

0

Một chút muộn cho chương trình.

xmlcutty cắt ra nút từ XML:

$ cat file.xml 
<?xml version="1.0" encoding="utf-8"?> 
<job xmlns="http://www.sample.com/">programming</job> 
<job xmlns="http://www.sample.com/">designing</job> 
<job xmlns="http://www.sample.com/">managing</job> 
<job xmlns="http://www.sample.com/">teaching</job> 

Các path tên tham số đường dẫn đến các yếu tố bạn muốn cắt ra. Trong trường hợp này, vì chúng ta không quan tâm đến các thẻ ở tất cả, chúng tôi đổi tên thẻ để \n, vì vậy chúng tôi có được một danh sách tốt đẹp:

$ xmlcutty -path /job -rename '\n' file.xml 
programming 
designing 
managing 
teaching 

Lưu ý, rằng XML là không hợp lệ để bắt đầu với (không có rễ thành phần). xmlcutty cũng có thể làm việc với XML bị hỏng một chút.

2

Sử dụng sed lệnh:

Ví dụ:

$ cat file.xml 
<note> 
     <to>Tove</to> 
       <from>Jani</from> 
       <heading>Reminder</heading> 
     <body>Don't forget me this weekend!</body> 
</note> 

$ cat file.xml | sed -ne '/<heading>/s#\s*<[^>]*>\s*##gp' 
Reminder 

Giải thích:

cat file.xml | sed -ne '/<pattern_to_find>/s#\s*<[^>]*>\s*##gp'

n - suppress in tất cả các dòng
e - kịch bản

/<pattern_to_find>/ - thấy dòng có chứa quy định mô hình những gì có thể ví dụ <heading>

tiếp theo là thay thế một phần s///p mà loại bỏ tất cả mọi thứ ngoại trừ giá trị mong muốn nơi / được thay thế bằng # để có thể đọc tốt hơn:

s#\s*<[^>]*>\s*##gp
\s* - bao gồm khoảng trắng nếu tồn tại (cùng ở cuối)
<[^>]*> đại diện cho <xml_tag> làm nguyên nhân thay thế regex không tham lam <.*?> không hoạt động cho sed
g - thay thế mọi thứ, ví dụ: đóng xml </xml_tag> thẻ

Các vấn đề liên quan