web-dev-qa-db-ja.com

bashでの文字のエスケープ(JSONの場合)

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には、さらに2つのフィールドがあります。

これは正常に機能しますが、もちろん、アポストロフィを含む上記のようなコミットメッセージがある場合、JSONは無効です。

Bashで必要な文字をエスケープするにはどうすればよいですか?私は言語に精通していないので、どこから始めればよいかわかりません。 '\'に置き換えると、少なくとも私が思うに仕事をするでしょう。

48
Rich Bradshaw

OK、どうしたらいいかわかりました。 Bashはこれを期待どおりネイティブにサポートしますが、いつものように、構文は実際にはあまり推測できません!

基本的に${string//substring/replacement}はイメージを返すので、使用できます

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

これをする。次の問題は、最初の正規表現がもう機能しないことですが、それは

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

結局、私はそれらを逃れる必要さえありませんでした。代わりに、JSONのすべての 'を\ "に交換しました。さて、あなたは毎日何かを学びます。

11
Rich Bradshaw

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"
55
polm23

データを適切に引用する方法を心配する代わりに、ファイルに保存し、@オプションで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'

42
chepner

jq これを行うことができます。

軽量、無料、Cで書かれたjqは、 GitHub で15,000個以上の星を使用したコミュニティサポートを幅広く楽しんでいます。個人的には、毎日のワークフローで非常に迅速で便利だと感じています。

文字列をJSONに変換

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

説明する、

  • -aは「ascii出力」を意味します
  • -Rは「生の入力」を意味します
  • .は「JSONドキュメントのルートを出力する」ことを意味します
  • <<<は文字列をstdinに渡します(bashのみ?)

Git + Grepのユースケース

OPで指定されたコード例を修正するには、jqをパイプ処理します。

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

私はこれに遭遇したときに、JSONを使用して転送するために、Bashで文字をエスケープしようとしていました。実際には、より大きな エスケープする必要がある文字のリスト があることがわかりました。特に、自由形式のテキストを処理しようとしている場合です。

便利だと思った2つのヒントがあります。

  • 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文字を正しくエスケープする方法を考え出していません。これを解決したら、答えを更新します。

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

上記の行は私のために機能します。 jqツールについては https://github.com/stedolan/jq を参照してください

7
wcy

私はそのようなものを見つけました:

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

最も簡単な方法は、 jshonコマンドラインツールを使用してJSONを解析、読み取り、作成することです

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

4
m-szalik

これは、バックスラッシュ(\)、二重引用符(")および制御文字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
0
Josh Bode

コミット後にコミットメッセージを含むメッセージを送信するという同じ考えがありました。最初に私は似たようなことを試みましたが、ここではautorとしてでした。しかし、後に、より優れたシンプルなソリューションが見つかりました。

メッセージを送信する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を呼び出します

0
KirillDE