在bash中转义字符(对于JSON)

时间:2012-04-07 10:28:19

标签: json bash escaping

我使用git,然后将提交消息和其他位作为JSON有效负载发布到服务器。

目前我有:

MSG=`git log -n 1 --format=oneline | grep -o ' .\+'`

将MSG设置为:

Calendar can't go back past today

然后

curl -i -X POST \
  -H 'Accept: application/text' \
  -H 'Content-type: application/json' \
  -d "{'payload': {'message': '$MSG'}}" \
  'https://example.com'

我真正的JSON有另外两个领域。

这样可以正常工作,但是当我有一个提交消息(如上面的撇号)时,JSON无效。

如何逃避bash中所需的字符?我不熟悉这种语言,所以不知道从哪里开始。用'替换\'至少可以做到我怀疑的工作。

12 个答案:

答案 0 :(得分:52)

使用Python:

这个解决方案不是纯粹的bash,但它是非侵入性的并处理unicode。

json_escape () {
    printf '%s' "$1" | python -c 'import json,sys; print(json.dumps(sys.stdin.read()))'
}

请注意,JSON是标准python库的一部分并且已经存在了很长时间,因此这是一个非常小的python依赖项。

或使用PHP:

json_escape () {
    printf '%s' "$1" | php -r 'echo json_encode(file_get_contents("php://stdin"));'
}

像这样使用:

$ json_escape "ヤホー"
"\u30e4\u30db\u30fc"

答案 1 :(得分:39)

不要担心如何正确引用数据,只需将其保存到文件中,然后使用@允许的curl结构--data选项。要确保正确转义git的输出以用作JSON值,请使用jq之类的工具生成JSON,而不是手动创建。

jq -n --arg msg "$(git log -n 1 --format=oneline | grep -o ' .\+')" \
   '{payload: { message: $msg }}' > git-tmp.txt

curl -i -X POST \
  -H 'Accept: application/text' \
  -H 'Content-type: application/json' \
  -d @git-tmp.txt \
  'https://example.com'

您还可以使用-d @-直接从标准输入中读取;我将此作为练习让读者构建从git读取的管道,并生成正确的有效负载消息以上传curl

(提示:它是jq ... | curl ... -d@- 'https://example.com'

答案 2 :(得分:16)

当我遇到这个时,我还试图在Bash中转义字符,以便使用JSON进行转移。我发现实际上有更大的list of characters that must be escaped - 特别是如果您正在尝试处理自由格式文本。

我发现有两个有用的提示:

  • 使用此主题中描述的Bash ${string//substring/replacement}语法。
  • 使用tab,newline,回车等实际控制字符。在vim中,您可以输入 Ctrl + V ,然后输入实际控制代码( Ctrl + I 例如标签。)

我想出的最终Bash替换如下:

JSON_TOPIC_RAW=${JSON_TOPIC_RAW//\\/\\\\} # \ 
JSON_TOPIC_RAW=${JSON_TOPIC_RAW//\//\\\/} # / 
JSON_TOPIC_RAW=${JSON_TOPIC_RAW//\'/\\\'} # ' (not strictly needed ?)
JSON_TOPIC_RAW=${JSON_TOPIC_RAW//\"/\\\"} # " 
JSON_TOPIC_RAW=${JSON_TOPIC_RAW//   /\\t} # \t (tab)
JSON_TOPIC_RAW=${JSON_TOPIC_RAW//
/\\\n} # \n (newline)
JSON_TOPIC_RAW=${JSON_TOPIC_RAW//^M/\\\r} # \r (carriage return)
JSON_TOPIC_RAW=${JSON_TOPIC_RAW//^L/\\\f} # \f (form feed)
JSON_TOPIC_RAW=${JSON_TOPIC_RAW//^H/\\\b} # \b (backspace)

我还没有在这个阶段弄清楚如何正确地转义Unicode字符,这也是(显然)需要的。如果我解决这个问题,我会更新我的答案。

答案 3 :(得分:11)

好的,找到了该怎么做。 Bash正如预期的那样本地支持它,虽然一如既往,语法并不是真的可以猜测!

基本上${string//substring/replacement}会返回您要成像的内容,因此您可以使用

MSG=${MSG//\'/\\\'}

要做到这一点。下一个问题是第一个正则表达式不再起作用,但可以用

替换
git log -n 1 --pretty=format:'%s'

最后,我甚至不需要逃避它们。相反,我只是把JSON中的所有'换成了'。嗯,你每天都学到一些东西。

答案 4 :(得分:11)

jq可以做到这一点。

轻量级,免费且用C语言编写,jq享有广泛的社区支持,GitHub上有超过12.5k的星星。我个人觉得它在我的日常工作流程中非常快速有用。

将字符串转换为JSON

jq -aR . <<< '猫に小判'

解释,

  • -a表示“ascii output”
  • -R表示“原始输入”
  • .表示“输出JSON文档的根目录”
  • <<<将字符串传递给stdin(仅限bash?)

Git + Curl用例

要修复OP给出的代码示例,只需通过jq。

MSG=`git log -n 1 --format=oneline | grep -o ' .\+' | jq -aR .`

答案 5 :(得分:6)

git log -n 1 --format=oneline | grep -o ' .\+' | jq --slurp --raw-input

以上这条线适合我。参考 https://github.com/stedolan/jq了解更多jq工具

答案 6 :(得分:4)

我找到了类似的东西:

MSG=`echo $MSG | sed "s/'/\\\\\'/g"`

答案 7 :(得分:4)

最简单的方法是使用jshon,一个命令行工具来解析,读取和创建JSON

jshon -s 'Your data goes here.' 2>/dev/null

答案 8 :(得分:0)

我有同样的想法在提交后发送带有提交消息的消息。 首先我尝试类似的是在这里作为autor。 但后来发现了一种更好,更简单的解决方案。

刚刚创建的php文件正在发送消息并用wget调用它。 in hooks / post-receive:

wget -qO - "http://localhost/git.php" 

在git.php中:

chdir("/opt/git/project.git");
$git_log = exec("git log -n 1 --format=oneline | grep -o ' .\+'");

然后创建JSON并以PHP样式调用CURL

答案 9 :(得分:0)

这是一个使用Perl的转义解决方案,它将反斜杠(\),双引号(")和控制字符U+0000转义为U+001F

$ echo -ne "Hello, \n\tBye" | \
  perl -pe 's/(\\(\\\\)*)/$1$1/g; s/(?!\\)(["\x00-\x1f])/sprintf("\\u%04x",ord($1))/eg;'
Hello, \u000a\u0009Bye

答案 10 :(得分:0)

我也遇到同样的问题。我试图在bash中的cURL的有效负载上添加一个变量,并且该变量一直以invalid_JSON的形式返回。在尝试了许多逃避技巧之后,我找到了解决问题的简单方法。答案全部在单引号和双引号中:

 curl --location --request POST 'https://hooks.slack.com/services/test-slack-hook' \
--header 'Content-Type: application/json' \
--data-raw '{"text":'"$data"'}'

也许对某人来说很方便!

答案 11 :(得分:0)

[...]中带有撇号,则JSON无效。

不符合https://www.json.org。 JSON字符串中允许使用单引号。

如何转义bash中所需的字符?

不是必需的。一切都与引号有关。

由于您没有明确标记这个问题,因此我的回答中也会包含xidel。就像curljq一样。
由于无法测试https://example.com,因此我将使用https://api.github.com/markdown(请参阅this答案)。

变量

$ msg=$(git log -n 1 --pretty=format:'%s')

$ curl -d '{"text":"'"$msg"'"}' https://api.github.com/markdown
$ curl -d "{\"text\":\"$msg\"}" https://api.github.com/markdown

$ xidel -s -d '{{"text":"'"$msg"'"}}' https://api.github.com/markdown -e '$raw'
$ xidel -s -d "{{\"text\":\"$msg\"}}" https://api.github.com/markdown -e '$raw'
$ xidel -s -e '
  x:request({
    "post":serialize-json({"text":"'"$msg"'"}),
    "url":"https://api.github.com/markdown"
  })/raw
'

命令替换

$ curl -d '{"text":"'"$(git log -n 1 --pretty=format:'%s')"'"}' https://api.github.com/markdown
$ curl -d "{\"text\":\"$(git log -n 1 --pretty=format:'%s')\"}" https://api.github.com/markdown

$ xidel -s -d '{{"text":"'"$(git log -n 1 --pretty=format:'%s')"'"}}' https://api.github.com/markdown -e '$raw'
$ xidel -s -d "{{\"text\":\"$(git log -n 1 --pretty=format:'%s')\"}}" https://api.github.com/markdown -e '$raw'
$ xidel -s -e '
  x:request({
    "post":serialize-json({"text":"'"$(git log -n 1 --pretty=format:'%s')"'"}),
    "url":"https://api.github.com/markdown"
  })/raw
'

管道

$ git log -n 1 --pretty=format:'%s' | xidel -s -d '{{"text":"{read()}"}}' https://api.github.com/markdown -e '$raw'
$ git log -n 1 --pretty=format:'%s' | xidel -s -d "{{\"text\":\"{read()}\"}}" https://api.github.com/markdown -e '$raw'
$ git log -n 1 --pretty=format:'%s' | xidel -s -e '
  x:request({
    "post":serialize-json({"text":read()}),
    "url":"https://api.github.com/markdown"
  })/raw
'

(也许curl也有可能,但我还没有找到方法)

所有13条命令应返回:<p>Calendar can't go back past today</p>