Tại sao nội dung JSON từ heredoc không thể phân tích cú pháp?


11

Tôi có một đoạn JSON.

Những điều sau đây không hoạt động:

VALUE=<<PERSON
{
  "type": "account",
  "customer_id": "1234",
  "customer_email": "jim@gmail.com"  
}
PERSON
echo -n "$VALUE" | python -m json.tool

Kết quả là:

Không có đối tượng JSON nào có thể được giải mã

Làm tương tự với jq, tức là

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

Không có đầu ra.

Có hành vi tương tự cho các điều sau đây:

VALUE=<<PERSON
'{
  "type": "account",
  "customer_id": "1234",
  "customer_email": "jim@gmail.com"  
}'
PERSON
echo -n "$VALUE" | python -m json.tool

Phản ứng:

Không có đối tượng JSON nào có thể được giải mã

Nhưng các công việc sau đây:

VALUE='{
  "type": "account",
  "customer_id": "1234",
  "customer_email": "jim@gmail.com"
}'
echo -n "$VALUE" | jq '.'
echo -n "$VALUE" | python -m json.tool

5
Tôi không biết bash đang làm gì, nhưng có một dấu phẩy sau chuỗi email trong hai lần đầu tiên của bạn nhưng không phải là lần thứ ba, điều này sẽ khiến cho cặp vợ chồng đầu tiên JSON bất hợp pháp
Nick T

@NickT bạn nên đưa ra câu trả lời vì tôi nghĩ đó chính xác là vấn đề.
rrauenza

Nếu đó là câu trả lời (duy nhất) thì có lẽ nên đóng lại vì "không thể sao chép (một lỗi đánh máy)". Tuy nhiên, có vẻ như câu trả lời của Kusa và terdon đề cập đến bài tập + chuyển hướng hoàn toàn bị hỏng nên bạn nhận được một chuỗi rỗng, do đó, có hai vấn đề, cả hai đều có cùng một lỗi "Không JSON ...". Đó là một thực tiễn rất tốt để chia đôi vấn đề bằng cách kiểm tra các giả định của bạn ở giữa: một đơn giản echo $VALUEmà không ... | jqcó thông tin.
Nick T

@NickT: Đó là vấn đề sao chép / dán. Xin lỗi vì sự nhầm lẫn
Jim

Câu trả lời:


19
VALUE=<<PERSON
some data
PERSON

echo "$VALUE"

Không có đầu ra.

Tài liệu ở đây là một chuyển hướng , bạn không thể chuyển hướng vào một biến.

Khi dòng lệnh được phân tích cú pháp, các chuyển hướng được xử lý trong một bước riêng biệt từ các phép gán biến. Do đó, lệnh của bạn tương đương với (lưu ý khoảng trắng)

VALUE= <<PERSON
some data
PERSON

Nghĩa là, nó gán một chuỗi rỗng cho biến của bạn, sau đó chuyển hướng đầu vào tiêu chuẩn từ chuỗi ở đây vào lệnh (nhưng không có lệnh, vì vậy không có gì xảy ra).

Lưu ý rằng

<<PERSON
some data
PERSON

là hợp lệ, như là

<somefile

Chỉ là không có lệnh nào có luồng đầu vào tiêu chuẩn có thể được đặt để chứa dữ liệu, vì vậy nó chỉ bị mất.

Điều này sẽ làm việc mặc dù:

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

Ở đây, lệnh nhận tài liệu ở đây là catvà nó sao chép nó vào đầu ra tiêu chuẩn của nó. Đây là những gì được gán cho biến bằng cách thay thế lệnh.

Trong trường hợp của bạn, thay vào đó bạn có thể sử dụng

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

mà không thực hiện thêm bước lưu trữ dữ liệu trong một biến.


2
Bạn cũng có thể chỉ cần làm PERSON="theo sau bởi một dòng mới và dữ liệu nhiều dòng, sau đó là một dòng khác "ở cuối.
R .. GitHub DỪNG GIÚP ICE

1
@R .. Có, nhưng tài liệu ở đây cho phép bạn bỏ qua các quy tắc trích dẫn của trình bao. Do đó, thường an toàn hơn khi sử dụng tài liệu ở đây thay vì chuỗi được trích dẫn cho dữ liệu nhiều dòng, đặc biệt nếu dữ liệu chứa dấu ngoặc đơn hoặc dấu ngoặc kép (hoặc cả hai).
Kusalananda

2
@R .. Với JSON mà chúng ta đang nói đến, có lẽ tốt hơn là sử dụng các dấu ngoặc đơn để không phải thoát dấu ngoặc kép của mỗi tên thuộc tính. PERSON='. Đó là trừ khi OP muốn nội suy các biến sau này.
JoL

(dấu gạch chéo ngược) (dòng mới) dường như biến mất trong tài liệu ở đây, ngay cả khi bạn trích dẫn / thoát khỏi từ phân cách. Đó có thể là mong muốn, nhưng có cách nào để vô hiệu hóa nó?
Scott

@Scott Nếu câu hỏi đó chưa được hỏi trên trang web này trước đây, nó sẽ là một câu hỏi xuất sắc theo đúng nghĩa của nó.
Kusalananda

11

Bởi vì biến không được đặt bởi heredoc của bạn:

$ VALUE=<<PERSON  
> {    
>   "type": "account",  
>   "customer_id": "1234",  
>   "customer_email": "jim@gmail.com",  
> }  
> PERSON
$ echo "$VALUE" 

$

Nếu bạn muốn sử dụng một di sản để gán giá trị cho một biến, bạn cần một cái gì đó như:

$ read -d '' -r VALUE <<PERSON  
{    
  "type": "account",  
  "customer_id": "1234",  
  "customer_email": "jim@gmail.com",  
}   
PERSON

1
Tại sao bạn gói dữ liệu JSON trong dấu ngoặc đơn? Nó không thực sự trông giống như OP muốn chúng là một phần của chuỗi đầu vào của anh ấy. Bên cạnh đó, +1 để cắt giảm số lượng mèo vô gia cư. Như với câu trả lời của Kusalananda, bạn có thể muốn đề xuất << \PERSONđể bảo vệ chống lại $s trong đầu vào và dấu gạch chéo ngược ở cuối dòng.
Scott

@Scott um, vì tôi chỉ sao chép một cách mù quáng văn bản từ OP. Cảm ơn
terdon

3
Đây là câu trả lời đúng. $(cat <<EOF ... EOF)là một cấu trúc kỳ lạ: chạy một subshell và sau đó gửi heredoc cho cat chỉ để nó gửi nó đến STDOUT và sau đó gán kết quả của subshell đó cho một biến? Tôi ước mọi người sẽ nghĩ về những gì họ nói về quá trình suy nghĩ của họ. Việc chỉ định một di truyền cho một biến qua read, bằng cách so sánh, là lành mạnh.
Giàu

Tôi sẽ không nói rằng, $(cat << EOF(dữ liệu), chuyện đó EOF )thật kỳ lạ. Thật là khó xử và hỗn láo, nhưng cũng vậy read -d … << EOF - đặc biệt read -d '' << EOF . Tôi đánh giá cao câu trả lời của terdon vì nó chỉ sử dụng nội dung, không có chương trình. Nhưng, quan trọng hơn, các bản $(cat << EOF (dữ liệu) EOF )không thành công nếu bất kỳ dòng nào kết thúc bằng \(dấu gạch chéo ngược) - xem các nhận xét dưới câu trả lời của Kusalananda .
Scott

4

Đó là bởi vì cách bạn đã định nghĩa một tài liệu ở đây để sử dụng với JSON là sai. Bạn cần sử dụng nó như

VALUE=$(cat <<EOF
{  
  "type": "account",  
  "customer_id": "1234",  
  "customer_email": "jim@gmail.com",  
}
EOF
)

và làm printf "$VALUE"nên đổ JSON như mong đợi.


3

Heredocs và các biến không trộn lẫn hoặc ít nhất là không theo cách này. Bạn có thể hoặc

Vượt qua di truyền như là đầu vào tiêu chuẩn của một ứng dụng

python -m json.tool <<PERSON  
{
  "type": "account",
  "customer_id": "1234",
  "customer_email": "jim@gmail.com",
}
PERSON

hoặc là…

Lưu trữ văn bản nhiều dòng trong một biến shell

VALUE='{
  "type": "account",
  "customer_id": "1234",
  "customer_email": "jim@gmail.com",
}'

Tôi đã sử dụng dấu ngoặc đơn để tránh sự cần thiết phải thoát khỏi dấu ngoặc kép bên trong. Tất nhiên bạn cũng có thể sử dụng dấu ngoặc kép, ví dụ nếu bạn cần mở rộng tham số:

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

Sau đó, bạn có thể sử dụng giá trị biến sau này.

echo -n "$VALUE" | python -m json.tool
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.