Por que o conteúdo JSON do heredoc não é analisável?

11

Eu tenho um fragmento JSON.

O seguinte não funciona:

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

O resultado é:

Nenhum objeto JSON pôde ser decodificado

Fazendo o mesmo com jq, ie

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

Não há saída.

Existe o mesmo comportamento para o seguinte:

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

Resposta:

Nenhum objeto JSON pôde ser decodificado

Mas o seguinte funciona:

VALUE='{
  "type": "account",
  "customer_id": "1234",
  "customer_email": "[email protected]"
}'
echo -n "$VALUE" | jq '.'
echo -n "$VALUE" | python -m json.tool
Jim
fonte
5
Eu não sei o que o bash está fazendo, mas há uma vírgula à direita após a seqüência de e-mail em seu primeiro dois, mas não no terceiro, o que tornaria o primeiro par JSON ilegal
Nick T
@ NickT, você deve fazer disso uma resposta, pois acho que esse é precisamente o problema.
Rrauenza 11/04
Se essa é a (única) resposta, provavelmente deve ser fechada como "não pode ser reproduzida (um erro de digitação)". No entanto, parece que a resposta de Kusa e Terdon menciona que a atribuição + redirecionamento está totalmente quebrada, de modo que você obtém uma string vazia; portanto, existem dois problemas, ambos com o mesmo erro "No JSON ...". É uma prática muito boa dividir problemas verificando suas suposições no meio: um simples echo $VALUEsem ... | jqseria informativo.
Nick T
@ NickT: Esse foi um problema de copiar / colar. Desculpe pela confusão
Jim

Respostas:

19
VALUE=<<PERSON
some data
PERSON

echo "$VALUE"

Sem saída.

Um documento aqui é um redirecionamento , você não pode redirecionar para uma variável.

Quando a linha de comando é analisada, os redirecionamentos são tratados em uma etapa separada das atribuições de variáveis. Seu comando é, portanto, equivalente a (observe o espaço)

VALUE= <<PERSON
some data
PERSON

Ou seja, ele atribui uma string vazia à sua variável e, em seguida, redireciona a entrada padrão da string here para o comando (mas não há comando, então nada acontece).

Observe que

<<PERSON
some data
PERSON

é válido, como é

<somefile

Só que não há nenhum comando cujo fluxo de entrada padrão possa ser definido para conter os dados, portanto, ele está perdido.

Isso funcionaria:

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

Aqui, o comando que recebe o documento here é cate o copia para sua saída padrão. É então o que é atribuído à variável por meio da substituição de comando.

No seu caso, você poderia usar

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

sem dar o passo extra de armazenar os dados em uma variável.

Kusalananda
fonte
2
Você também pode simplesmente PERSON="seguir uma nova linha e os dados de várias linhas, depois outra "no final.
R .. GitHub Pare de ajudar o gelo
1
@R .. Sim, mas um documento aqui permite que você ignore as regras de cotação do shell. Portanto, muitas vezes é mais seguro usar um documento aqui em vez de uma string entre aspas para dados de várias linhas, especialmente se os dados contiverem aspas simples ou duplas (ou ambas).
Kusalananda
2
@R .. Dado o JSON que estamos falando, pode ser melhor usar aspas simples para não precisar escapar das aspas duplas de cada nome de propriedade. PERSON='. Isso a menos que o OP queira interpolar variáveis ​​mais tarde.
JOL
(barra invertida) (nova linha) parece desaparecer em um documento aqui, mesmo que você cite / escape da palavra delimitadora. Isso pode ser desejável, mas existe alguma maneira de desativá-lo?
Scott
@ Scott Se essa pergunta não tiver sido feita neste site antes, seria uma excelente pergunta por si só.
Kusalananda
11

Como a variável não está sendo definida pelo seu heredoc:

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

$

Se você deseja usar um heredoc para atribuir um valor a uma variável, precisa de algo como:

$ read -d '' -r VALUE <<PERSON  
{    
  "type": "account",  
  "customer_id": "1234",  
  "customer_email": "[email protected]",  
}   
PERSON
terdon
fonte
1
Por que você está agrupando os dados JSON entre aspas simples? Realmente não parece que o OP queira que eles façam parte de sua string de entrada. Além disso, +1 por reduzir a população de gatos sem-teto. Como na resposta de Kusalananda, você pode sugerir << \PERSONproteção contra $s na entrada e barras invertidas no final das linhas.
Scott
@ Scott, porque eu apenas copiei cegamente o texto do OP. Obrigado
terdon
3
Esta é a resposta certa. $(cat <<EOF ... EOF)é uma construção estranha: executar um subshell e, em seguida, enviar um heredoc para o gato apenas para enviá-lo para STDOUT e, em seguida, atribuir o resultado desse subshell a uma variável? Eu gostaria que as pessoas pensassem no que estão dizendo sobre seus processos de pensamento. A atribuição de um heredoc a uma variável por meio readde comparação é sensata.
Rich
Eu não diria que $(cat << EOF… (dados)… EOF )é estranho. É estranho e complicado, mas também é read -d … << EOF - especialmente read -d '' << EOF . Agradeço a resposta de Terdon, porque ele usa apenas builtins, sem programas. Mas, mais importante, o $(cat << EOF… (dados)… EOF )falha se alguma linha termina com \(barra invertida) - veja os comentários na resposta de Kusalananda .
Scott
4

Isso ocorre porque a maneira como você definiu um documento aqui para usar com um JSON está errada. Você precisa usá-lo como

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

e fazer printf "$VALUE"deve despejar o JSON conforme o esperado.

Inian
fonte
3

Heredocs e variáveis ​​não se misturam bem ou pelo menos não dessa maneira. Você também pode…

Passe o heredoc como a entrada padrão de um aplicativo

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

ou…

Armazenar texto de várias linhas em uma variável de shell

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

Usei aspas simples para evitar a necessidade de escapar das aspas duplas internas. Claro que você também pode usar aspas duplas, por exemplo, se precisar expandir parâmetros:

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

Então você pode usar o valor da variável mais tarde.

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