nikkie-ftnextの日記

イベントレポートや読書メモを発信

bashで文字列リテラルを並べると、つながった1つの文字列が作れる(JSON形式の文字列を例に)

Today I Learnedです

  • bashで文字列リテラルを並べるとつながる
  • bashの文字列リテラルを並べてつなげるのを利用してJSON形式の文字列も作れるが、私はjqコマンド派
    • 正常系はいいが、異常系(ダブルクォートを含む文字列など)の対応が大変と感じたため

目次

bashの文字列

デフォルトがzshmacOSですが、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
          )

例えばDjangosettings.pyINSTALLED_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 -Rraw-input
https://www.tohoho-web.com/ex/jq.html#opt-raw-input

入力を JSON としてではなく、改行で区切られた文字列の集合として扱います。

$ echo $value | jq -R
"ho\"ge"

これでjqではダブルクォートを含んだ文字列リテラルとして扱えます(バックスラッシュにご注目!)
jqに引き渡してJSONにしました(ここで.は文字列を指しますね)