Mengapa konten JSON dari heredoc tidak dapat diuraikan?

11

Saya memiliki fragmen JSON.

Berikut ini tidak berfungsi:

VALUE=<<PERSON
{
  "type": "account",
  "customer_id": "1234",
  "customer_email": "[email protected]"  
}
PERSON
echo -n "$VALUE" | python -m json.tool

Hasilnya adalah:

Tidak ada objek JSON yang bisa diterjemahkan

Melakukan hal yang sama dengan jq, yaitu

echo -n "$VALUE" | jq '.'

Tidak ada output.

Ada perilaku yang sama untuk yang berikut:

VALUE=<<PERSON
'{
  "type": "account",
  "customer_id": "1234",
  "customer_email": "[email protected]"  
}'
PERSON
echo -n "$VALUE" | python -m json.tool

Tanggapan:

Tidak ada objek JSON yang bisa diterjemahkan

Tetapi berikut ini berfungsi:

VALUE='{
  "type": "account",
  "customer_id": "1234",
  "customer_email": "[email protected]"
}'
echo -n "$VALUE" | jq '.'
echo -n "$VALUE" | python -m json.tool
Jim
sumber
5
Saya tidak tahu apa yang dilakukan bash, tetapi ada koma di belakang setelah string email di dua string pertama Anda tetapi tidak pada yang ketiga, yang akan membuat pasangan pertama ilegal JSON
Nick T
@NickT Anda harus membuat jawaban karena saya pikir itulah masalahnya.
rrauenza
Jika itulah (satu-satunya) jawaban itu mungkin harus ditutup karena "tidak dapat direproduksi (salah ketik)". Namun, sepertinya jawaban Kusa dan terdon menyebutkan penugasan + redirection benar-benar rusak sehingga Anda mendapatkan string kosong, jadi ada dua masalah, keduanya akan memberikan kesalahan "No JSON ..." yang sama. Ini adalah praktik yang sangat baik untuk membagi dua masalah dengan memeriksa asumsi Anda di tengah: yang sederhana echo $VALUEtanpa ... | jqakan informatif.
Nick T
@NickT: Itu masalah copy / paste. Maaf atas kebingungan
Jim

Jawaban:

19
VALUE=<<PERSON
some data
PERSON

echo "$VALUE"

Tidak ada output.

Dokumen di sini adalah pengalihan , Anda tidak dapat mengarahkan ulang ke variabel.

Ketika baris perintah diuraikan, pengalihan ditangani dalam langkah terpisah dari penugasan variabel. Karena itu perintah Anda setara dengan (perhatikan spasi)

VALUE= <<PERSON
some data
PERSON

Yaitu, ia memberikan string kosong ke variabel Anda, kemudian mengarahkan input standar dari string-sini ke dalam perintah (tetapi tidak ada perintah, jadi tidak ada yang terjadi).

Catat itu

<<PERSON
some data
PERSON

valid, sebagaimana adanya

<somefile

Hanya saja tidak ada perintah yang aliran input standarnya dapat diatur untuk berisi data, sehingga hilang begitu saja.

Ini akan berhasil:

VALUE=$(cat <<PERSON
some data
PERSON
)

Di sini, perintah yang menerima dokumen-sini adalah cat, dan menyalinnya ke output standarnya. Inilah yang kemudian ditugaskan ke variabel melalui substitusi perintah.

Dalam kasus Anda, Anda bisa menggunakan

python -m json.tool <<END_JSON
JSON data here
END_JSON

tanpa mengambil langkah ekstra menyimpan data dalam suatu variabel.

Kusalananda
sumber
2
Anda juga bisa hanya PERSON="diikuti oleh baris baru dan data multi-baris, lalu yang lain "di akhir.
R .. GitHub BERHENTI MEMBANTU ICE
1
@ R .. Ya, tetapi dokumen di sini memungkinkan Anda untuk mem-bypass aturan penawaran shell. Oleh karena itu seringkali lebih aman untuk menggunakan dokumen di sini daripada string yang dikutip untuk data multi-line, terutama jika data tersebut berisi penawaran tunggal atau ganda (atau keduanya).
Kusalananda
2
@R .. Mengingat ini adalah JSON yang sedang kita bicarakan, mungkin lebih baik menggunakan tanda kutip tunggal untuk tidak harus melepaskan diri dari tanda kutip ganda dari setiap nama properti. PERSON='. Itu kecuali OP ingin menginterpolasi variabel nanti.
JoL
(backslash) (baris baru) tampaknya hilang dalam dokumen di sini, bahkan jika Anda mengutip / menghindari kata pembatas. Itu mungkin diinginkan, tetapi apakah ada cara untuk menonaktifkannya?
Scott
@Scott Jika pertanyaan itu belum pernah diajukan di situs ini sebelumnya, itu akan menjadi pertanyaan yang sangat bagus.
Kusalananda
11

Karena variabel tidak disetel oleh heredoc Anda:

$ VALUE=<<PERSON  
> {    
>   "type": "account",  
>   "customer_id": "1234",  
>   "customer_email": "[email protected]",  
> }  
> PERSON
$ echo "$VALUE" 

$

Jika Anda ingin menggunakan heredoc untuk memberikan nilai pada variabel, Anda perlu sesuatu seperti:

$ read -d '' -r VALUE <<PERSON  
{    
  "type": "account",  
  "customer_id": "1234",  
  "customer_email": "[email protected]",  
}   
PERSON
terdon
sumber
1
Mengapa Anda membungkus data JSON dalam tanda kutip tunggal? Itu tidak benar-benar terlihat seperti OP ingin mereka menjadi bagian dari string masukannya. Selain itu, +1 untuk mengurangi populasi kucing tunawisma. Seperti dengan jawaban Kusalananda, Anda mungkin ingin menyarankan << \PERSONuntuk melindungi terhadap $input dan backslash di ujung baris.
Scott
@ Hemat um, karena saya hanya membabi buta menyalin teks dari OP. Terima kasih
terdon
3
Ini adalah jawaban yang benar. $(cat <<EOF ... EOF)itu konstruk yang aneh: menjalankan subkulit dan kemudian mengirim heredoc ke kucing sehingga mengirimkannya ke STDOUT dan kemudian menetapkan hasil subkulit itu ke variabel? Saya berharap orang berpikir tentang apa yang mereka katakan tentang proses pemikiran mereka. Menetapkan heredoc ke variabel melalui read, sebagai perbandingan, adalah waras.
Kaya
Saya tidak akan mengatakan bahwa $(cat << EOF... (data) ... EOF )aneh. Ini canggung dan berbelit-belit, tetapi begitu read -d … << EOF - terutama read -d '' << EOF . Saya menghargai jawaban terdon karena hanya menggunakan builtin, tidak ada program. Tetapi, yang lebih penting, $(cat << EOF... (data) ... EOF )gagal jika ada garis yang diakhiri dengan \(garis miring terbalik) - lihat komentar di bawah jawaban Kusalananda .
Scott
5

Itu karena cara Anda mendefinisikan dokumen di sini untuk digunakan dengan JSON salah. Anda harus menggunakannya sebagai

VALUE=$(cat <<EOF
{  
  "type": "account",  
  "customer_id": "1234",  
  "customer_email": "[email protected]",  
}
EOF
)

dan melakukan printf "$VALUE"harus membuang JSON seperti yang diharapkan.

Inian
sumber
3

Variabel dan variabel Hered tidak tercampur dengan baik atau setidaknya tidak dengan cara ini. Anda bisa ...

Lulus heredoc sebagai input standar aplikasi

python -m json.tool <<PERSON  
{
  "type": "account",
  "customer_id": "1234",
  "customer_email": "[email protected]",
}
PERSON

atau…

Menyimpan teks multi-baris dalam variabel shell

VALUE='{
  "type": "account",
  "customer_id": "1234",
  "customer_email": "[email protected]",
}'

Saya menggunakan tanda kutip tunggal untuk menghindari perlunya lolos dari tanda kutip ganda batin. Tentu saja Anda juga dapat menggunakan tanda kutip ganda, misalnya jika Anda perlu memperluas parameter:

VALUE="{
  \"type\": \"account\",
  \"customer_id\": ${ID},
  \"customer_email\": \"${EMAIL}\",
}"

Kemudian Anda bisa menggunakan nilai variabel nanti.

echo -n "$VALUE" | python -m json.tool
David Foerster
sumber