为什么heredoc中的JSON内容不可解析?


11

我有一个JSON片段。

以下内容不起作用:

VALUE=<<PERSON
{
  "type": "account",
  "customer_id": "1234",
  "customer_email": "jim@gmail.com"  
}
PERSON
echo -n "$VALUE" | python -m json.tool

结果是:

JSON对象无法解码

对进行相同的操作jq,即

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

没有输出。

对于以下情况,存在相同的行为:

VALUE=<<PERSON
'{
  "type": "account",
  "customer_id": "1234",
  "customer_email": "jim@gmail.com"  
}'
PERSON
echo -n "$VALUE" | python -m json.tool

响应:

JSON对象无法解码

但是以下工作原理:

VALUE='{
  "type": "account",
  "customer_id": "1234",
  "customer_email": "jim@gmail.com"
}'
echo -n "$VALUE" | jq '.'
echo -n "$VALUE" | python -m json.tool

5
我不知道bash在做什么,但是在前两个电子邮件字符串后面有一个逗号结尾,但在第三个字符串后面没有逗号,这会使前两个成为非法JSON
Nick T

@NickT您应该做出一个答案,因为我认为这正是问题所在。
rrauenza

如果那是(唯一的)答案,则可能应将其关闭,因为“无法复制(错别字)”。但是,看起来Kusa和terdon的答案提到分配+重定向已完全中断,所以您得到一个空字符串,因此存在两个问题,这两个问题都将产生相同的“ No JSON ...”错误。通过在中间检查您的假设来平分问题是一个很好的实践:一个简单的echo $VALUEnot ... | jq会提供很多信息。
尼克T

@NickT:那是复制/粘贴问题。抱歉给您带来的困惑
Jim

Answers:


19
VALUE=<<PERSON
some data
PERSON

echo "$VALUE"

无输出。

此处文档是重定向,您不能重定向到变量。

解析命令行后,重定向将在与变量分配不同的步骤中处理。因此,您的命令等效于(注意空格)

VALUE= <<PERSON
some data
PERSON

也就是说,它将为您的变量分配一个空字符串,然后将标准输入从此处字符串重定向到命令中(但是没有命令,因此什么也没有发生)。

注意

<<PERSON
some data
PERSON

是有效的

<somefile

只是没有命令可以将其标准输入流设置为包含数据,因此它丢失了。

但这可以工作:

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

在这里,接收here-document的命令是cat,并将其复制到其标准输出。然后通过命令替换将其分配给变量。

在您的情况下,您可以改用

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

无需采取将数据存储在变量中的额外步骤。


2
您还可以先执行PERSON="换行和多行数据,然后再执行另一行"
R .. GitHub停止帮助ICE

1
@R ..是的,但是这里文档可让您绕过Shell的引用规则。因此,对于多行数据,使用此处文档而不是带引号的字符串通常会更安全,尤其是在数据包含单引号或双引号(或两者都包含)的情况下。
Kusalananda

2
@R。鉴于我们正在谈论的是JSON,因此最好使用单引号而不用对每个属性名称的双引号进行转义。PERSON='。除非OP希望稍后插入变量。
JoL

即使您引用/转义分隔符单词,(反斜杠)(换行符)在here文档中也似乎消失了。这可能是理想的,但是有什么办法可以禁用它?
斯科特,

@Scott如果以前在此站点上未曾提出过该问题,那么它本身就是一个很好的问题。
Kusalananda

11

由于您的Heredoc并未设置变量,因此:

$ VALUE=<<PERSON  
> {    
>   "type": "account",  
>   "customer_id": "1234",  
>   "customer_email": "jim@gmail.com",  
> }  
> PERSON
$ echo "$VALUE" 

$

如果要使用heredoc为变量分配值,则需要类似以下内容:

$ read -d '' -r VALUE <<PERSON  
{    
  "type": "account",  
  "customer_id": "1234",  
  "customer_email": "jim@gmail.com",  
}   
PERSON

1
为什么将JSON数据包装在单引号中?看起来OP并不希望它们成为他输入字符串的一部分。除此之外,+ 1用于减少无家可归的猫的数量。与Kusalananda的答案一样,您可能希望建议<< \PERSON防止$输入中的s和行尾的反斜杠。
斯科特,

@Scott um,因为我只是盲目地从OP复制了文本。谢谢
terdon

3
这是正确的答案。$(cat <<EOF ... EOF)是一个怪异的构造:运行一个子shell,然后将一个Heredoc发送给cat,以便将其发送到STDOUT,然后将该子shell的结果分配给一个变量?我希望人们会思考他们对思想过程的看法。read通过比较,将Heredoc分配给变量是明智的。
Rich

我不会说$(cat << EOF……(数据)…… EOF )很奇怪。它很笨拙而且令人费解,但是read -d … << EOF -尤其如此read -d '' << EOF 。我很欣赏terdon的回答,因为它只使用内置函数,而不使用程序。但是,更重要的是,如果任何行以(反斜杠)结尾,则$(cat << EOF…(数据)…将EOF )失败\—请参阅Kusalananda的回答下的注释。
斯科特(Scott)

5

这是因为您定义用于JSON的here-doc的方式是错误的。您需要将其用作

VALUE=$(cat <<EOF
{  
  "type": "account",  
  "customer_id": "1234",  
  "customer_email": "jim@gmail.com",  
}
EOF
)

并且printf "$VALUE"应该按预期转储JSON。


3

Heredocs和变量不能很好地混合,或者至少不能以这种方式混合。您可以…

将heredoc传递为应用程序的标准输入

python -m json.tool <<PERSON  
{
  "type": "account",
  "customer_id": "1234",
  "customer_email": "jim@gmail.com",
}
PERSON

要么…

将多行文本存储在shell变量中

VALUE='{
  "type": "account",
  "customer_id": "1234",
  "customer_email": "jim@gmail.com",
}'

我使用单引号来避免对内部双引号进行转义。当然,您也可以使用双引号,例如,如果需要扩展参数:

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

然后,您可以稍后使用变量值。

echo -n "$VALUE" | python -m json.tool
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.