Today I Learnedです
- bashで文字列リテラルを並べるとつながる
- bashの文字列リテラルを並べてつなげるのを利用してJSON形式の文字列も作れるが、私は
jq
コマンド派- 正常系はいいが、異常系(ダブルクォートを含む文字列など)の対応が大変と感じたため
目次
bashの文字列
デフォルトがzshのmacOSですが、bash
で確認しています。
% bash bash-3.2$
以下ではbashのプロンプトを$
と省略します
$ echo HelloWorld HelloWorld $ echo "HelloWorld" HelloWorld
man echo
より
The echo utility writes any specified operands, separated by single blank (‘ ’) characters and followed by a newline (‘\n’) character, to the standard output.
$ echo Hello World Hello World
2つのoperand(HelloとWorld)の間に半角スペースを入れて出力されました。
bashの文字列はクォートを使っても表せますよね。
(違いはダブルクォートだと変数が展開できる)
$ echo "Hello" 'World' Hello World
ではこちらはどうでしょう?
$ echo "Hello"'World'
文字列リテラルの間に半角スペースを入れませんでした。
見慣れない感じですよね。
出力は
HelloWorld
文字列Hello
と文字列World
からなる文字列(HelloWorld
)が出力されているという理解です。
脱線:Pythonの文字列リテラルも並べてつながる
https://docs.python.org/ja/3/reference/lexical_analysis.html#string-literal-concatenation
文字列やバイト列リテラルは、互いに異なる引用符を使っていても (空白文字で区切っても) 複数隣接させることができます。
この機能を使うと、バックスラッシュを減らしたり、長い文字列を手軽に分離して複数行にまたがらせたり、あるいは部分文字列ごとにコメントを追加することさえできます。
上記URLにある、部分文字列ごとにコメントを追加する例
re.compile("[A-Za-z_]" # letter or underscore "[A-Za-z0-9_]*" # letter, digit or underscore )
例えばDjangoのsettings.py
のINSTALLED_APPS
でカンマを忘れると、この機能によってひとつなぎの文字列として扱われます。
JSON形式の文字列
$ jq --version jq-1.6
bashの文字列を利用
上記のクォートが並ぶ書き方を使うと、JSON形式の文字列が書けます(今回の学びとしてはあまりオススメしません)。
$ value='hoge' $ echo '{"key": "'"${value}"'"}' | jq '.' { "key": "hoge" }
{"key": "
で1つの文字列- 変数
value
をダブルクォートの中に展開した文字列("${value}"
) "}
で1つの文字列
これで文字列{"key": "hoge"}
となり、jq
でパースできます。
ちなみに、これまでの私は以下のようにやっていました。
変数を展開できるダブルクォートを使ったうえで、JSONで必須のダブルクォートは全部エスケープ
$ echo "{\"key\": \"${value}\"}" | jq '.' { "key": "hoge" }
紹介したつなげる方法の落とし穴はこんなケースです。
$ value='ho"ge' $ echo '{"key": "'"${value}"'"}' {"key": "ho"ge"} $ echo '{"key": "'"${value}"'"}' | jq '.' parse error: Invalid numeric literal at line 1, column 15
文字列中のダブルクォートがエスケープされていないので、悪さをしています。
sed
などで加工が必要になっています。
jq
を活用
$ echo $value | jq -R | jq '{"key": .}' { "key": "ho\"ge" }
jq -R
はraw-input
https://www.tohoho-web.com/ex/jq.html#opt-raw-input
入力を JSON としてではなく、改行で区切られた文字列の集合として扱います。
$ echo $value | jq -R "ho\"ge"
これでjq
ではダブルクォートを含んだ文字列リテラルとして扱えます(バックスラッシュにご注目!)
jq
に引き渡してJSONにしました(ここで.
は文字列を指しますね)