Regex dengan perintah sed untuk mem-parsing teks json

15

Saya punya teks json ini:

{
    "buildStatus" : {
        "status" : "ERROR",
        "conditions" : [{
                "status" : "OK",
                "metricKey" : "bugs"
            }, {
                "status" : "ERROR",
                "metricKey" : "test_success_density"
            }, {
                "status" : "OK",
                "metricKey" : "vulnerabilities"
            }
        ],
        "periods" : []
    }
}

Saya ingin mengekstrak status keseluruhan buildStatus, yaitu output yang diharapkan adalah "KESALAHAN"

"buildStatus" : {
    "status" : "ERROR",
    ....
}

Saya mencoba ekspresi sed di bawah, tetapi tidak berhasil, ia kembali OK:

status= sed -E 's/.*\"buildStatus\":.*\"status\":\"([^\"]*)\",.*/\1/' jsonfile

Apa yang saya lakukan salah?

pengguna1876040
sumber

Jawaban:

16

Jangan parsing struktur data bersarang yang kompleks seperti JSON atau XML dengan ekspresi reguler, gunakan parser JSON yang tepat jshon.

Pertama, Anda perlu menginstalnya:

sudo apt-get install jshon

Maka Anda harus memberikannya data JSON untuk diurai melalui input standar, sehingga Anda dapat mengarahkan output perintah lain di sana dengan pipa ( |) atau mengarahkan file ke sana ( < filename).

Argumen yang diperlukan untuk mengekstrak data yang Anda inginkan terlihat seperti ini:

jshon -e "buildStatus" -e "status" -u
  • -e "buildStatus" mengambil elemen dengan indeks "buildStatus" dari kamus tingkat atas.
  • -e "status" mengambil elemen dengan indeks "status" dari kamus tingkat kedua yang dipilih di atas.
  • -u mengonversi data yang dipilih dari JSON ke data biasa (yaitu di sini menghapus tanda kutip di sekitar string)

Jadi perintah yang Anda jalankan, tergantung dari mana Anda mendapatkan datanya, terlihat seperti salah satunya:

jshon -e "buildStatus" -e "status" -u < YOUR_INPUT_FILE
YOUR_JSON_PRODUCING_COMMAND | jshon -e "buildStatus" -e "status" -u

Untuk mempelajari lebih lanjut jshon, Anda dapat membaca halaman manualnya yang dapat diakses secara online di sini atau hanya dengan mengetik man jshon.

Komandan Byte
sumber
6
Ada juga jq:jq -r .buildStatus.status
muru
@ HTNW Saya tidak pernah menyukai jawaban itu, karena "tag terbuka XML tunggal" (yang merupakan pertanyaan yang diajukan) adalah bahasa biasa (dan Anda pada prinsipnya dapat membuat parser XML lengkap dengan menggunakan regex untuk mencocokkan tag, komentar, cdata bagian, dan menggunakan tumpukan sederhana untuk menangani konteks bersarang). Namun, bahasa reguler yang paling 'menarik' di JSON adalah string literal.
Acak832
10

Pekerjaan untuk jq:

jq -r '.["buildStatus"]["status"]' file.json

Dapat disingkat menjadi:

jq -r '.buildStatus.status' file.json

-r( --raw-output) Menghasilkan string tanpa jsonpemformatan string yaitu tanpa tanda kutip.

Contoh:

% cat file.json                   
{
    "buildStatus" : {
        "status" : "ERROR",
        "conditions" : [{
                "status" : "OK",
                "metricKey" : "bugs"
            }, {
                "status" : "ERROR",
                "metricKey" : "test_success_density"
            }, {
                "status" : "OK",
                "metricKey" : "vulnerabilities"
            }
        ],
        "periods" : []
    }
}

% jq -r '.["buildStatus"]["status"]' file.json
ERROR

% jq -r '.buildStatus.status' file.json       
ERROR

Jika belum diinstal, instal dengan (tersedia di gudang Universe):

sudo apt-get install jq 
heemayl
sumber
8

Seperti yang telah disebutkan, parsing data terstruktur kompleks lebih disukai dengan API yang sesuai. Python memiliki jsonmodul untuk itu, yang secara pribadi saya gunakan cukup banyak dalam skrip saya, dan cukup mudah untuk mengekstrak bidang yang diinginkan seperti yang Anda inginkan:

$ python -c 'import sys,json;print json.load(sys.stdin)["buildStatus"]["status"]' <  input.txt
ERROR

Apa yang terjadi di sini adalah bahwa kita mengarahkan file input ke stdin python, dan membacanya dengan json.load(). Itu menjadi kamus python dengan kunci "buildStatus", dan itu berisi kamus python lain dengan kunci "status". Jadi, kami hanya mencetak nilai kunci dalam kamus yang disimpan dalam kamus lain. Cukup sederhana.

Selain dari kesederhanaan, keuntungan lain adalah python dan API ini semuanya sudah diinstal sebelumnya dan disertakan dengan Ubuntu secara default.

Sergiy Kolodyazhnyy
sumber
6

Anda sebenarnya dapat melakukan ini sed, tetapi saya sangat mendorong Anda untuk menggunakan bahasa yang lebih canggih yang memiliki alat yang ditulis untuk menangani data JSON. Anda bisa mencoba perl atau python, misalnya.

Sekarang, dalam contoh sederhana Anda, yang Anda inginkan adalah kemunculan pertama "status", sehingga Anda dapat melakukan:

$ sed -nE '/status/{s/.*:\s*"(.*)",/\1/p;q}' file.json 
ERROR

Caranya adalah dengan menggunakan -nuntuk menghindari pencetakan, maka jika garis cocok status( /status/), Anda menghapus semuanya kecuali bagian yang Anda inginkan s/.*:\s*"(.*)",/\1/, ppotong garis dan quit.


Secara pribadi, saya menemukan perintah grep yang setara ini jauh lebih sederhana:

$ grep -m1 -oP '"status"\s*:\s*"\K[^"]+' file.json 
ERROR

Atau yang ini:

$ perl -ne 'if(s/.*"status"\s*:\s*"([^"]+).*/$1/){print;exit}' file.json 
ERROR

Namun serius, jika Anda berencana untuk mem-parsing file JSON, jangan coba lakukan ini secara manual. Gunakan parser JSON yang tepat.

terdon
sumber
atau yang ini:grep -m 1 status file.json | tr -cd '[[:alnum:]]:' | cut -f2 -d':'
slowko
1
@ user1876040 sama-sama Harap ingat untuk menerima salah satu jawaban (saya sarankan ByteCommander , ini adalah solusi yang lebih baik) sehingga pertanyaan dapat ditandai sebagai dijawab).
terdon
6

Tidak mengatakan Anda harus menggunakan sed(saya pikir seseorang telah menurunkan saya hanya karena tidak menulis peringatan wajib) tetapi, jika Anda perlu mencari sesuatu di baris berikutnyabuildStatus karena Anda tampaknya mencoba dalam usaha Anda sendiri, Anda perlu memberitahu seduntuk membaca baris selanjutnya dengan Nperintah

$ sed -rn '/buildStatus/N;s/.*buildStatus.*\n.*: "(.*)",/\1/p' file
ERROR

Catatan:

  • -n jangan cetak apa pun sampai kami memintanya
  • -rgunakan ERE (sama seperti -E)
  • /buildStatus/N temukan pola ini dan baca baris selanjutnya juga
  • s/old/new/ menggantikan old dengannew
  • .* sejumlah karakter apa pun di telepon
  • \n garis baru
  • : "(.*)",simpan semua karakter yang terjadi di antara : "dan",
  • \1 referensi kembali ke pola yang disimpan
  • p cetak bagian yang kami kerjakan
Zanna
sumber
0

Ada penjelasan khas mengapa sed dan alat pengolah aliran teks yang serupa tidak dilengkapi dengan baik untuk menguraikan data terstruktur seperti JSON dan XML. Saya tidak memiliki itu di tangan, tetapi itu ada di luar sana, dan saya percaya intinya adalah bahwa ekspresi yang dibutuhkan dalam semua tetapi mungkin situasi paling sedikit dengan cepat menjadi sangat kompleks, sementara alat-alat alternatif yang dibangun khusus untuk mengurai struktur lebih elegan, mudah dibaca, dan efisien di parsing yang sama.

Seperti muru telah memberikan komentar , jqharus menjadi alat yang tepat untuk pekerjaan itu. Saya juga dapat menjaminnya secara pribadi sangat bersemangat untuk melihatnya menggantikan beberapa kali di mana saya telah mencoba mengurai data yang sama untuk hampir tidak ada atau membebani kesuksesan. Itu bahkan berisi tentang kemampuan untuk memformat dan mengendalikan output. Saya lebih suka jsontoolkarena suatu alasan atau lebih yang saya lupa saat ini.

Komandan Byte tampaknya merekomendasikan jshondalam jawaban lain . Saya belum pernah menggunakan alat itu, tetapi mengingatkan saya pada xmlstarletdan sintaksnya, juga dengan beberapa presentasi yang dapat disesuaikan untuk hasilnya.

Pisis
sumber
Anda mungkin berbicara tentang stackoverflow.com/a/1732454/2072269
muru
3
Pertimbangkan untuk meningkatkan jawaban Anda dengan menunjukkan contoh bagaimana jsontooldapat digunakan untuk kasus spesifik OP
Sergiy Kolodyazhnyy
Lol @muru, benar, itu adalah salah satu postingan yang berusaha untuk mencegah penggunaan parsing XML / JSON dengan Regex! Saya lebih merekomendasikan jqbahwa muru dan heemayl menggambarkan yang sudah memiliki exmaples, dan hanya memposting alasan di baliknya: askubuntu.com/a/863948/230721
Pysis
0

Hanya alat Json lain yang disebut json ( https://github.com/trentm/json )

$ json buildStatus.status < file.json
ERROR

Studi kasus ini menyesatkan: sepertinya alat tidak berfungsi. Anda juga dapat menggunakan jsonuntuk mengubah file json:

$ json -e 'this.buildStatus.status="not error"' < file.json > new.json

atau bahkan...

$ json -e 'this.buildStatus.status="no errors"' < file.json | json -e 'this.buildStatus.status
no errors

dokumentasi di: http://trentm.com/json/


jika tidak dipasang:

  • instal simpul
  • dan sudo npm install -g json

sumber