74

我正在使用 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 中所需的字符?我不熟悉这种语言,所以不知道从哪里开始。替换'\'我怀疑至少可以完成这项工作。

4

12 回答 12

75

使用 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"
于 2012-11-20T03:25:55.653 回答
75

jq可以做到这一点。

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

将字符串转换为 JSON

$ echo '猫に小判' | jq -aRs .
"\u732b\u306b\u5c0f\u5224\n"
$ printf 'ô\nè\nà\n' | jq -Rs .
"ô\nè\nà\n"

解释,

  • -a表示“ascii 输出”(在第二个示例中省略)
  • -R意思是“原始输入”
  • -s表示“包括换行符”(助记符:“slurp”)
  • .表示“输出 JSON 文档的根”

Git + Grep 用例

要修复 OP 给出的代码示例,只需通过 jq 进行管道传输。

MSG=`git log -n 1 --format=oneline | grep -o ' .\+' | jq -aRs .`
于 2018-05-16T22:18:14.260 回答
59

无需担心如何正确引用数据,只需将其保存到文件并使用允许选项的@构造即可。为确保 的输出正确转义以用作 JSON 值,请使用生成 JSON 之类的工具,而不是手动创建它。curl--datagitjq

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'

于 2012-07-15T21:27:18.887 回答
19

当我遇到这个时,我还试图在 Bash 中转义字符,以便使用 JSON 进行传输。我发现实际上有一个更大的字符列表必须被转义——尤其是当你试图处理自由格式的文本时。

我发现有两个有用的技巧:

  • 使用此线程中描述的 Bash${string//substring/replacement}语法。
  • 对制表符、换行符、回车符等使用实际控制字符。在 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 字符,这也是(显然)需要的。如果我解决了这个问题,我会更新我的答案。

于 2012-07-15T20:59:41.650 回答
15

OK, found out what to do. Bash supports this natively as expected, though as always, the syntax isn't really very guessable!

Essentially ${string//substring/replacement} returns what you'd image, so you can use

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

To do this. The next problem is that the first regex doesn't work anymore, but that can be replaced with

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

In the end, I didn't even need to escape them. Instead, I just swapped all the ' in the JSON to \". Well, you learn something every day.

于 2012-04-07T11:10:20.090 回答
9
git log -n 1 --format=oneline | grep -o ' .\+' | jq --slurp --raw-input

上面的行对我有用。更多工具请参考 https://github.com/stedolan/jqjq

于 2016-11-08T06:34:17.253 回答
4

我发现了类似的东西:

MSG=`echo $MSG | sed "s/'/\\\\\'/g"`
于 2012-04-07T10:34:45.640 回答
4

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

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

于 2016-02-20T00:28:16.673 回答
2

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

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

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

您可以使用正确准备要发布的 JSON。
由于https://example.com无法测试,我将使用https://api.github.com/markdown(请参阅答案)作为示例。

让我们假设'çömmít' "mêssågè"作为 的奇异输出git log -n 1 --pretty=format:'%s'

"text"使用正确转义的 -attribute值创建(序列化)JSON 对象:

$ git log -n 1 --pretty=format:'%s' | \
  xidel -se 'serialize({"text":$raw},{"method":"json","encoding":"us-ascii"})'
{"text":"'\u00E7\u00F6mm\u00EDt' \"m\u00EAss\u00E5g\u00E8\""}

卷曲(可变)

$ eval "$(
  git log -n 1 --pretty=format:'%s' | \
  xidel -se 'msg:=serialize({"text":$raw},{"method":"json","encoding":"us-ascii"})' --output-format=bash
)"

$ echo $msg
{"text":"'\u00E7\u00F6mm\u00EDt' \"m\u00EAss\u00E5g\u00E8\""}

$ curl -d "$msg" https://api.github.com/markdown
<p>'çömmít' "mêssågè"</p>

卷曲(管道)

$ git log -n 1 --pretty=format:'%s' | \
  xidel -se 'serialize({"text":$raw},{"method":"json","encoding":"us-ascii"})' | \
  curl -d@- https://api.github.com/markdown
<p>'çömmít' "mêssågè"</p>

实际上,curl如果您已经在使用xidel.

西德尔(管)

$ git log -n 1 --pretty=format:'%s' | \
  xidel -s \
  -d '{serialize({"text":read()},{"method":"json","encoding":"us-ascii"})}' \
  "https://api.github.com/markdown" \
  -e '$raw'
<p>'çömmít' "mêssågè"</p>

Xidel(管道,查询中)

$ git log -n 1 --pretty=format:'%s' | \
  xidel -se '
    x:request({
      "post":serialize(
        {"text":$raw},
        {"method":"json","encoding":"us-ascii"}
      ),
      "url":"https://api.github.com/markdown"
    })/raw
  '
<p>'çömmít' "mêssågè"</p>

Xidel(全部在查询中)

$ xidel -se '
  x:request({
    "post":serialize(
      {"text":system("git log -n 1 --pretty=format:'\''%s'\''")},
      {"method":"json","encoding":"us-ascii"}
    ),
    "url":"https://api.github.com/markdown"
  })/raw
'
<p>'çömmít' "mêssågè"</p>
于 2020-03-27T17:10:36.547 回答
1

我在同样的问题上苦苦挣扎。我试图在 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"'}'

也许它对某人有用!

于 2020-02-28T12:13:19.907 回答
0

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

$ echo -ne "Hello, \n\tBye" | \
  perl -pe 's/(\\(\\\\)*)/$1$1/g; s/(?!\\)(["\x00-\x1f])/sprintf("\\u%04x",ord($1))/eg;'
Hello, \u000a\u0009Bye
于 2017-08-15T00:29:46.400 回答
0

我有同样的想法,在提交后发送一条带有提交消息的消息。首先,我在这里尝试过类似的尝试。但后来找到了更好更简单的解决方案。

刚刚创建了发送消息的 php 文件并使用 wget 调用它。在钩子/接收后:

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

于 2017-03-18T14:05:29.193 回答