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": "[email protected]"  
}
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": "[email protected]"  
}'
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": "[email protected]"
}'
echo -n "$VALUE" | jq '.'
echo -n "$VALUE" | python -m json.tool
Jim
źródło
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.

Kusalananda
źródło
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": "[email protected]",  
> }  
> 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": "[email protected]",  
}   
PERSON
terdon
źródło
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": "[email protected]",  
}
EOF
)

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

Inian
źródło
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": "[email protected]",
}
PERSON

lub…

Przechowuj tekst wielowierszowy w zmiennej powłoki

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

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
David Foerster
źródło