Dlaczego treść JSON z heredoc nie jest przetwarzalna?


11

Mam fragment JSON.

Następujące nie działa:

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

Wynik to:

Żaden obiekt JSON nie mógł zostać zdekodowany

Robiąc to samo z jq, tj

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

Brak wyników.

To samo zachowanie dotyczy:

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

Odpowiedź:

Żaden obiekt JSON nie mógł zostać zdekodowany

Ale następujące prace:

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

5
Nie wiem, co robi bash, ale po pierwszych dwóch e-mailach występuje przecinek końcowy, ale nie trzeci, co spowodowałoby, że pierwsza para byłaby nielegalna JSON
— Nick T

@NickT powinieneś udzielić odpowiedzi, ponieważ uważam, że to właśnie jest problem.
— rrauenza

Jeśli jest to (jedyna) odpowiedź, prawdopodobnie należy ją zamknąć, ponieważ „nie można jej odtworzyć (literówka)”. Wygląda jednak na to, że odpowiedź Kusa i terdona wspominają, że przypisanie + przekierowanie jest całkowicie zepsute, więc otrzymujesz pusty ciąg znaków, więc istnieją dwa problemy, z których oba dawałyby ten sam błąd „Brak JSON ...”. Dobrą praktyką jest dzielenie problemów przez sprawdzanie swoich założeń w środku: proste echo $VALUEbez ... | jqbyłoby pouczające.
— Nick T

@NickT: To był problem z kopiowaniem / wklejaniem. Przepraszam za zamieszanie
— Jim

Odpowiedzi:


19
VALUE=<<PERSON
some data
PERSON

echo "$VALUE"

Brak danych wyjściowych.

Dokument tutaj jest przekierowaniem , nie można przekierować do zmiennej.

Podczas analizowania wiersza poleceń przekierowania są obsługiwane w oddzielnym kroku niż przypisania zmiennych. Twoje polecenie jest zatem równoważne z (zwróć uwagę na spację)

VALUE= <<PERSON
some data
PERSON

Oznacza to, że przypisuje pusty ciąg do zmiennej, a następnie przekierowuje standardowe wejście z ciągu tutaj do polecenia (ale nie ma polecenia, więc nic się nie dzieje).

Zauważ, że

<<PERSON
some data
PERSON

jest ważne, jak jest

<somefile

Tyle, że nie ma polecenia, którego standardowy strumień wejściowy można ustawić tak, aby zawierał dane, więc został po prostu utracony.

Działa to jednak:

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

Tutaj jest polecenie, które odbiera dokument tutaj cati kopiuje go na standardowe wyjście. To jest następnie przypisywane do zmiennej poprzez podstawienie polecenia.

W twoim przypadku możesz zamiast tego użyć

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

bez wykonywania dodatkowego kroku przechowywania danych w zmiennej.


2
Możesz też po prostu PERSON="wstawić znak nowej linii i dane wieloliniowe, a następnie kolejną "na końcu.
— R .. GitHub ZATRZYMAJ LÓD

1
@R .. Tak, ale dokument tutaj pozwala ominąć reguły cytowania powłoki. Dlatego często bezpieczniej jest używać dokumentu tutaj zamiast ciągu cytowanego w przypadku danych wieloliniowych, szczególnie jeśli dane zawierają pojedyncze lub podwójne cudzysłowy (lub oba).
— Kusalananda

2
@R .. Biorąc pod uwagę, że mówimy o JSON, może być lepiej użyć pojedynczych cudzysłowów, aby nie musieć unikać podwójnych cudzysłowów w nazwach każdej właściwości. PERSON='. To chyba, że ​​OP chce później interpolować zmienne.
— JoL

(odwrotny ukośnik) (nowa linia) wydaje się znikać w dokumencie tutaj, nawet jeśli zacytujesz / unikniesz słowa ogranicznika. Może to być pożądane, ale czy można to wyłączyć?
— Scott

@ Scott Jeśli to pytanie nie było wcześniej zadawane na tej stronie, byłoby to samo w sobie doskonałe pytanie.
— Kusalananda

11

Ponieważ zmienna nie jest ustawiana przez heredoc:

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

$

Jeśli chcesz użyć heredoc do przypisania wartości do zmiennej, potrzebujesz czegoś takiego:

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

1
Dlaczego owijasz dane JSON pojedynczymi cudzysłowami? Tak naprawdę nie wygląda na to, że OP chce, aby były częścią jego ciągu wejściowego. Oprócz tego +1 za ograniczenie populacji bezdomnych kotów. Podobnie jak w przypadku odpowiedzi Kusalanandy, możesz zaproponować << \PERSONochronę przed $s na wejściu i odwrotnymi ukośnikami na końcach linii.
— Scott

@ Scott, bo właśnie ślepo skopiowałem tekst z OP. Dzięki
— terdon

3
To jest właściwa odpowiedź. $(cat <<EOF ... EOF)to dziwna konstrukcja: uruchamianie podpowłoki, a następnie wysyłanie heredoc do kota w celu wysłania go do STDOUT, a następnie przypisanie wyniku tej podpowłoki do zmiennej? Chciałbym, żeby ludzie myśleli o tym, co mówią o swoich procesach myślowych. Przypisanie heredoc do zmiennej poprzez read, dla porównania, jest rozsądne.
— Bogaty

Nie powiedziałbym, że $(cat << EOF… (dane)… EOF )jest dziwny. Jest niewygodny i zawiły, ale taki też jest read -d … << EOF - szczególnie read -d '' << EOF . Doceniam odpowiedź terdona, ponieważ używa tylko wbudowanych programów, bez programów. Ale, co ważniejsze, $(cat << EOF… (dane)… EOF )zawodzi, jeśli jakaś linia kończy się na \(odwrotny ukośnik) - patrz komentarze pod odpowiedzią Kusalanandy .
— Scott,

5

Jest tak, ponieważ sposób, w jaki zdefiniowałeś tutaj dokument do użycia z JSON, jest zły. Musisz użyć go jako

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

i wykonanie printf "$VALUE"powinno zrzucić JSON zgodnie z oczekiwaniami.


3

Heredoki i zmienne nie mieszają się dobrze, a przynajmniej nie w ten sposób. Możesz albo…

Przekaż heredoc jako standardowe wejście aplikacji

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

lub…

Przechowuj tekst wielowierszowy w zmiennej powłoki

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

Użyłem pojedynczych cudzysłowów, aby uniknąć konieczności ucieczki od wewnętrznych podwójnych cudzysłowów. Oczywiście możesz także użyć podwójnych cudzysłowów, np. Jeśli potrzebujesz rozszerzyć parametry:

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

Następnie możesz później użyć wartości zmiennej.

echo -n "$VALUE" | python -m json.tool
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.