Pourquoi le contenu JSON de heredoc n'est-il pas analysable?

11

J'ai un fragment JSON.

Ce qui suit ne fonctionne pas:

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

Le résultat est:

Aucun objet JSON n'a pu être décodé

Faire de même avec jq, c.-à-d.

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

Il n'y a pas de sortie.

Il existe le même comportement pour les éléments suivants:

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

Réponse:

Aucun objet JSON n'a pu être décodé

Mais les travaux suivants:

VALUE='{
  "type": "account",
  "customer_id": "1234",
  "customer_email": "[email protected]"
}'
echo -n "$VALUE" | jq '.'
echo -n "$VALUE" | python -m json.tool
Jim
la source
5
Je ne sais pas ce que fait bash, mais il y a une virgule de fin après la chaîne de courrier électronique dans vos deux premiers mais pas sur le troisième, ce qui rendrait le premier couple illégal JSON
Nick T
@NickT, vous devriez en faire une réponse car je pense que c'est précisément le problème.
rrauenza
Si c'est la (seule) réponse, elle devrait probablement être fermée car "ne peut pas être reproduit (une faute de frappe)". Cependant, il semble que la réponse de Kusa et de terdon mentionne que l'affectation + la redirection est totalement rompue, vous obtenez donc une chaîne vide, il y a donc deux problèmes, qui donneraient tous deux la même erreur "No JSON ...". C'est une très bonne pratique de diviser les problèmes en vérifiant vos hypothèses au milieu: un simple echo $VALUEsans ... | jqserait instructif.
Nick T
@NickT: C'était un problème de copier / coller. Désolé pour la confusion
Jim

Réponses:

19
VALUE=<<PERSON
some data
PERSON

echo "$VALUE"

Pas de sortie.

Un document ici est une redirection , vous ne pouvez pas rediriger vers une variable.

Lorsque la ligne de commande est analysée, les redirections sont gérées dans une étape distincte des affectations de variables. Votre commande est donc équivalente à (notez l'espace)

VALUE= <<PERSON
some data
PERSON

Autrement dit, il affecte une chaîne vide à votre variable, puis redirige l'entrée standard de la chaîne here-string vers la commande (mais il n'y a pas de commande, donc rien ne se passe).

Notez que

<<PERSON
some data
PERSON

est valide, tel quel

<somefile

C'est juste qu'il n'y a pas de commande dont le flux d'entrée standard peut être défini pour contenir les données, il est donc juste perdu.

Cela fonctionnerait cependant:

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

Ici, la commande qui reçoit le document here est cat, et le copie sur sa sortie standard. C'est alors ce qui est affecté à la variable au moyen de la substitution de commande.

Dans votre cas, vous pouvez utiliser à la place

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

sans prendre l'étape supplémentaire de stockage des données dans une variable.

Kusalananda
la source
2
Vous pouvez également simplement faire PERSON="suivi d'un retour à la ligne et des données multilignes, puis d'un autre "à la fin.
R .. GitHub STOP STOPINGING ICE
1
@R .. Oui, mais un document ici vous permet de contourner les règles de citation du shell. Il est donc souvent plus sûr d'utiliser un document ici au lieu d'une chaîne entre guillemets pour les données multilignes, surtout si les données contiennent des guillemets simples ou doubles (ou les deux).
Kusalananda
2
@R .. Étant donné qu'il s'agit de JSON dont nous parlons, il pourrait être préférable d'utiliser des guillemets simples pour ne pas avoir à échapper aux guillemets doubles de chaque nom de propriété. PERSON='. C'est à moins que l'OP ne veuille interpoler des variables plus tard.
JoL
(barre oblique inverse) (nouvelle ligne) semble disparaître dans un document ici, même si vous citez / échappez le mot délimiteur. Cela peut être souhaitable, mais existe-t-il un moyen de le désactiver?
Scott
@Scott Si cette question n'a pas été posée sur ce site auparavant, ce serait une excellente question à part entière.
Kusalananda
11

Parce que la variable n'est pas définie par votre hérédoc:

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

$

Si vous souhaitez utiliser un hérédoc pour affecter une valeur à une variable, vous avez besoin de quelque chose comme:

$ read -d '' -r VALUE <<PERSON  
{    
  "type": "account",  
  "customer_id": "1234",  
  "customer_email": "[email protected]",  
}   
PERSON
terdon
la source
1
Pourquoi encapsulez-vous les données JSON entre guillemets simples? Il ne semble pas vraiment que l'OP veuille qu'ils fassent partie de sa chaîne d'entrée. Mis à part cela, +1 pour réduire la population de chats sans-abri. Comme pour la réponse de Kusalananda, vous voudrez peut-être suggérer << \PERSONde vous protéger contre les $s dans l'entrée et les barres obliques inverses à la fin des lignes.
Scott
@ Scott euh, parce que je viens de copier aveuglément le texte de l'OP. Merci
terdon
3
C'est la bonne réponse. $(cat <<EOF ... EOF)est une construction bizarre: exécuter un sous-shell puis envoyer un hérédoc à cat juste pour qu'il l'envoie à STDOUT et ensuite attribuer le résultat de ce sous-shell à une variable? J'aimerais que les gens réfléchissent à ce qu'ils disent de leurs processus de pensée. Affecter un hérédoc à une variable via read, par comparaison, est sain.
Rich
Je ne dirais pas que $(cat << EOF… (données)… EOF )est bizarre. C'est maladroit et alambiqué, mais c'est le cas read -d … << EOF - surtout read -d '' << EOF . J'apprécie la réponse de terdon car il n'utilise que des programmes intégrés, pas de programmes. Mais, plus important encore, les $(cat << EOF… (données)… EOF )échouent si des lignes se terminent par \(barre oblique inverse) - voir les commentaires sous la réponse de Kusalananda .
Scott
5

C'est parce que la façon dont vous avez défini un document ici à utiliser avec un JSON est incorrecte. Vous devez l'utiliser comme

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

et faire printf "$VALUE"devrait vider le JSON comme prévu.

Inian
la source
3

Heredocs et variables ne se mélangent pas bien ou du moins pas de cette façon. Tu peux soit…

Passer l'hérédoc comme entrée standard d'une application

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

ou…

Stocker du texte sur plusieurs lignes dans une variable shell

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

J'ai utilisé des guillemets simples pour éviter d'avoir à échapper aux guillemets doubles internes. Bien sûr, vous pouvez également utiliser des guillemets doubles, par exemple si vous devez étendre les paramètres:

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

Vous pourrez ensuite utiliser la valeur de la variable ultérieurement.

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