UnixツールでJSONを解析する 質問する

UnixツールでJSONを解析する 質問する

次のように、curl リクエストから返された JSON を解析しようとしています。

curl 'http://twitter.com/users/username.json' |
    sed -e 's/[{}]/''/g' | 
    awk -v k="text" '{n=split($0,a,","); for (i=1; i<=n; i++) print a[i]}'

上記は JSON をフィールドに分割します。例:

% ...
"geo_enabled":false
"friends_count":245
"profile_text_color":"000000"
"status":"in_reply_to_screen_name":null
"source":"web"
"truncated":false
"text":"My status"
"favorited":false
% ...

特定のフィールド ( で示される-v k=text) を印刷するにはどうすればよいですか?

ベストアンサー1

コマンドラインからJSONを操作するために特別に設計されたツールが数多くあり、Awkで行うよりもはるかに簡単で信頼性が高い。jq:

curl -s 'https://api.github.com/users/lambda' | jq -r '.name'

また、Pythonのようにシステムにすでにインストールされているツールを使ってこれを行うこともできます。jsonモジュール、適切な JSON パーサーの利点を維持しながら、余分な依存関係を回避します。以下では、元の JSON がエンコードされる UTF-8 を使用することを前提としています。これは、ほとんどの最新の端末でも使用されています。

Python3:

curl -s 'https://api.github.com/users/lambda' | \
    python3 -c "import sys, json; print(json.load(sys.stdin)['name'])"

Python 2:

export PYTHONIOENCODING=utf8
curl -s 'https://api.github.com/users/lambda' | \
    python2 -c "import sys, json; print json.load(sys.stdin)['name']"

よくある質問

純粋なシェルソリューションではダメなのでしょうか?

標準POSIX/シングルUnix仕様シェルは非常に限定された言語であり、シーケンス(リストまたは配列)や連想配列(他の言語ではハッシュテーブル、マップ、辞書、またはオブジェクトとも呼ばれる)を表現する機能がありません。このため、ポータブルシェルスクリプトでJSONの解析結果を表現するのはやや困難です。ややハック的なやり方ただし、キーまたは値に特定の特殊文字が含まれていると、多くのコードが壊れる可能性があります。

Bash 4 以降、zsh、ksh は配列と連想配列をサポートしていますが、これらのシェルはどこでも利用できるわけではありません (macOS は GPLv2 から GPLv3 への変更により Bash 3 で Bash の更新を停止しましたが、多くの Linux システムには zsh がすぐにインストールされていません)。Bash 4 または zsh (どちらも最近のほとんどの macOS、Linux、BSD システムで使用可能) のいずれかで動作するスクリプトを作成することは可能ですが、そのような多言語スクリプトで動作するシェバン ラインを作成するのは難しいでしょう。

最後に、シェルで本格的な JSON パーサーを作成すると、依存関係がかなり大きくなるため、代わりに jq や Python などの既存の依存関係を使用する方がよいでしょう。適切な実装を行うには、1 行のコード、または 5 行の短いコードでさえ不十分です。

awk、sed、grep を使わないのはなぜですか?

これらのツールを使用すると、1 行に 1 つのキーなど、既知の形式で既知の形状の JSON から簡単に抽出することができます。他の回答には、これに関する提案の例がいくつかあります。

ただし、これらのツールは行ベースまたはレコード ベースの形式用に設計されており、エスケープ文字を含む一致する区切り文字の再帰解析用に設計されていません。

したがって、awk/sed/grep を使用したこれらの手抜きのソリューションは脆弱である可能性が高く、空白の折りたたみ、JSON オブジェクトへのネスト レベルの追加、文字列内のエスケープされた引用符など、入力形式の一部が変更されると機能しなくなります。すべての JSON 入力を機能しなくなることなく処理できるほど堅牢なソリューションもかなり大きく複雑になるため、jqまたは Python への別の依存関係を追加することとそれほど違いはありません。

以前、シェル スクリプトでの入力解析が不十分だったために大量の顧客データが削除された経験があるので、このように脆弱になる可能性のある手っ取り早い方法はお勧めしません。1 回限りの処理を行う場合は、他の回答の提案を参照してください。ただし、既存のテスト済み JSON パーサーを使用することを強くお勧めします。

歴史的記録

この回答は元々推奨されていましたjsawkは動作するはずですが、 よりも少し使いにくく、jqスタンドアロンの JavaScript インタープリタがインストールされている必要がありますが、これは Python インタープリタほど一般的ではないため、上記の回答の方がおそらく望ましいでしょう。

curl -s 'https://api.github.com/users/lambda' | jsawk -a 'return this.name'

この回答も元々は質問の Twitter API を使用していましたが、その API は機能しなくなったため、例をコピーしてテストすることが難しく、新しい Twitter API には API キーが必要なため、API キーなしで簡単に使用できる GitHub API を使用するように切り替えました。元の質問に対する最初の回答は次のようになります。

curl 'http://twitter.com/users/username.json' | jq -r '.text'

おすすめ記事