LoginSignup
3
0

More than 3 years have passed since last update.

JSONをwhile read lineしてjqしててハマった話

Posted at

JSONの中身の配列を、while read line で逐次処理していて、エラーになりました。

parse error: Invalid numeric literal at line 1, column 335

TL;DR

read に -rオプションを付けたら解決しました。

while read -r line;do
    : #処理
done

原因は、String値の中の\"のバックスラッシュが外れてしまっていた為です。
readがバックスラッシュをエスケープ文字として扱う為こうなってしまいます。
-r をつけることで、\をただの\の文字として変数格納してくれます。

コード

上手くいかなかったコードです。
AWSCLIのdescribe-stacksからCloudFormationスタック達を逐次処理してやろうとしていた訳です。

# functions
each_elm_in_array(){
        jq -c '.[]'
}

# main
stacks=$(aws cloudformation \
        describe-stacks \
        --query Stacks \
        --output json
)

echo "$stacks" | each_elm_in_array | while read stack;do
        stack_name=$(echo "$stack" | jq -r .StackName)
        echo "$stack" | jq '.Parameters | map(.ParameterKey + "=" + .ParameterValue)?' > $stack_name.json
done

既存のCloudFormationスタックからパラメータを抜き出して、aws cloudformation deployコマンドに渡せるjsonファイルを作ります。
上手くいく方はこちら、つってもreadに-rオプション付けただけ。

# functions
each_elm_in_array(){
        jq -c '.[]'
}

# main
stacks=$(aws cloudformation \
        $aws_region_option \
        $aws_profile_option \
        describe-stacks \
        --query Stacks \
        --output json
)

echo "$stacks" | each_elm_in_array | while read -r stack;do
        stack_name=$(echo "$stack" | jq -r .StackName)
        echo "$stack" | jq '.Parameters | map(.ParameterKey + "=" + .ParameterValue)?' > $stack_name.json
done

解説

each_elm_in_array は、JSON配列を受け取ることを予期しています。.[]で配列の要素を取り出して分解しています、-cオプションでその分解した一要素を一行にしている感じです。
あとは、while read stackに渡して、1要素(=1行)ずつ処理していってます。

エラーが起きたのは、下から2行目の echo "$stack" | jq ... のところでした。
エラーの原因となったJSON文字列を覗いてみると、以下のような部分が、、、
"Description":"IAM role for serviceaccount "k8s-namespace/external-dns" [created and managed by eksctl]" ←エラーの時
"Description":"IAM role for serviceaccount \"k8s-namespace/external-dns\" [created and managed by eksctl]" ←本来こうなってて欲しかった
文字列の中の"\"のようにエスケープされてないといけませんがバックスラッシュが外れていました。これが原因でjqのパースエラーが起こっていた訳です。

あー、どこでエスケープ外れてしまったんだろう。。と思っていたらreadコマンドでstack変数に格納するところでした。
readコマンドは、デフォルトではバックスラッシュをエスケープ文字として解釈するんですね。。。うーんこの仕様って救われる人よりトラブる人の方が多いのでは?

所感

shellスクリプトでJSONの中身の配列を逐次処理したいことって結構あるのではないかと思うので、そんな時にはハマりやすいポイントなのでは?と思って投稿しました。sedしてreadに渡してもいいんですけどパイプするコマンド一個増えてなんかそれもなーと思ったので、readのオプションで対処できてよかったですね。
while read line する時は基本何も考えず-rオプション付けた方がトラブらないで済みそうだなーとも思いました。

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0