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.