¿Por qué el contenido JSON de heredoc no se puede analizar?

11

Tengo un fragmento JSON.

Lo siguiente no funciona:

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

El resultado es:

Ningún objeto JSON podría decodificarse

Haciendo lo mismo con jq, es decir

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

No hay salida.

Hay el mismo comportamiento para lo siguiente:

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

Respuesta:

Ningún objeto JSON podría decodificarse

Pero lo siguiente funciona:

VALUE='{
  "type": "account",
  "customer_id": "1234",
  "customer_email": "[email protected]"
}'
echo -n "$VALUE" | jq '.'
echo -n "$VALUE" | python -m json.tool
Jim
fuente
55
No sé qué está haciendo bash, pero hay una coma final después de la cadena de correo electrónico en los dos primeros, pero no en el tercero, lo que haría que los primeros pares sean ilegales JSON
Nick T
@NickT deberías responder a eso, ya que creo que ese es precisamente el problema.
rrauenza
Si esa es la (única) respuesta, probablemente debería cerrarse como "no se puede reproducir (un error tipográfico)". Sin embargo, parece que la respuesta de Kusa y Terdon menciona que la asignación + redirección está totalmente rota, por lo que obtienes una cadena vacía, por lo que hay dos problemas, los cuales darían el mismo error "No JSON ...". Es una buena práctica dividir los problemas revisando sus suposiciones en el medio: un simple echo $VALUEsin ... | jqsería informativo.
Nick T
@NickT: Ese fue un problema de copiar / pegar. Perdón por la confusión
Jim

Respuestas:

19
VALUE=<<PERSON
some data
PERSON

echo "$VALUE"

Ninguna salida.

Un documento aquí es una redirección , no se puede redirigir a una variable.

Cuando se analiza la línea de comando, las redirecciones se manejan en un paso separado de las asignaciones variables. Por lo tanto, su comando es equivalente a (tenga en cuenta el espacio)

VALUE= <<PERSON
some data
PERSON

Es decir, asigna una cadena vacía a su variable, luego redirige la entrada estándar de la cadena aquí al comando (pero no hay comando, por lo que no sucede nada).

Tenga en cuenta que

<<PERSON
some data
PERSON

es válido, como es

<somefile

Es solo que no hay un comando cuya secuencia de entrada estándar se pueda configurar para contener los datos, por lo que simplemente se pierde.

Sin embargo, esto funcionaría:

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

Aquí, el comando que recibe el documento here es cat, y lo copia a su salida estándar. Esto es lo que se asigna a la variable mediante la sustitución del comando.

En su caso, podría usar

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

sin dar el paso adicional de almacenar los datos en una variable.

Kusalananda
fuente
2
También podría hacerlo PERSON="seguido de una nueva línea y los datos de varias líneas, luego otro "al final.
R .. GitHub DEJA DE AYUDAR A HIELO
1
@R .. Sí, pero un documento aquí le permite omitir las reglas de cotización del shell. Por lo tanto, a menudo es más seguro usar un documento aquí en lugar de una cadena entre comillas para datos de varias líneas, especialmente si los datos contienen comillas simples o dobles (o ambas).
Kusalananda
2
@R .. Dado que estamos hablando de JSON, podría ser mejor usar comillas simples para no tener que escapar de las comillas dobles de cada nombre de propiedad. PERSON='. Eso a menos que el OP quiera interpolar variables más tarde.
JoL
(barra invertida) (nueva línea) parece desaparecer en un documento aquí, incluso si cita / escapa la palabra delimitador. Eso puede ser deseable, pero ¿hay alguna forma de desactivarlo?
Scott
@Scott Si esa pregunta no se ha hecho antes en este sitio, sería una excelente pregunta por derecho propio.
Kusalananda
11

Debido a que la variable no está siendo establecida por su heredoc:

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

$

Si desea utilizar un heredoc para asignar un valor a una variable, necesita algo como:

$ read -d '' -r VALUE <<PERSON  
{    
  "type": "account",  
  "customer_id": "1234",  
  "customer_email": "[email protected]",  
}   
PERSON
terdon
fuente
1
¿Por qué envuelve los datos JSON entre comillas simples? Realmente no parece que el OP quiera que sean parte de su cadena de entrada. Aparte de eso, +1 por reducir la población de gatos sin hogar. Al igual que con la respuesta de Kusalananda, es posible que desee sugerir << \PERSONproteger contra $s en la entrada y barras diagonales en los extremos de las líneas.
Scott
@Scott um, porque acabo de copiar a ciegas el texto del OP. Gracias
terdon
3
Esta es la respuesta correcta. $(cat <<EOF ... EOF)es una construcción extraña: ¿ejecutar un subshell y luego enviar un heredoc a cat solo para que lo envíe a STDOUT y luego asignar el resultado de ese subshell a una variable? Desearía que las personas piensen en lo que dicen sobre sus procesos de pensamiento. Asignar un heredoc a una variable vía read, en comparación, es sensato.
Rico
Yo no diría que $(cat << EOF... (datos) ... EOF )es raro. Es incómodo y complicado, pero también lo es read -d … << EOF , especialmente read -d '' << EOF . Agradezco la respuesta de terdon porque solo usa programas incorporados, no programas. Pero, lo que es más importante, el $(cat << EOF... (datos) ... EOF )falla si alguna línea termina con \(barra invertida) - vea los comentarios bajo la respuesta de Kusalananda .
Scott
5

Esto se debe a que la forma en que ha definido un documento aquí para usar con un JSON es incorrecta. Necesitas usarlo como

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

y hacer printf "$VALUE"debería volcar el JSON como se esperaba.

Inian
fuente
3

Los heredocs y las variables no se mezclan bien o al menos no de esta manera. Tu también puedes…

Pase el heredoc como entrada estándar de una aplicación

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

o…

Almacenar texto de varias líneas en una variable de shell

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

Usé comillas simples para evitar la necesidad de escapar de las comillas dobles internas. Por supuesto, también puede usar comillas dobles, por ejemplo, si necesita expandir los parámetros:

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

Entonces puede usar el valor de la variable más adelante.

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