web-dev-qa-db-ja.com

シェルスクリプトでJSONデータを読み取る

シェルでは、次の形式のJSON応答を読み取る必要があるという要件があります。

 { "Messages": [ { "Body": "172.16.1.42|/home/480/1234/5-12-2013/1234.toSort", "ReceiptHandle": "uUk89DYFzt1VAHtMW2iz0VSiDcGHY+H6WtTgcTSgBiFbpFUg5lythf+wQdWluzCoBziie8BiS2GFQVoRjQQfOx3R5jUASxDz7SmoCI5bNPJkWqU8ola+OYBIYNuCP1fYweKl1BOFUF+o2g7xLSIEkrdvLDAhYvHzfPb4QNgOSuN1JGG1GcZehvW3Q/9jq3vjYVIFz3Ho7blCUuWYhGFrpsBn5HWoRYE5VF5Bxc/zO6dPT0n4wRAd3hUEqF3WWeTMlWyTJp1KoMyX7Z8IXH4hKURGjdBQ0PwlSDF2cBYkBUA=", "MD5OfBody": "53e90dc3fa8afa3452c671080569642e", "MessageId": "e93e9238-f9f8-4bf4-bf5b-9a0cae8a0ebc" } ] }

ここでは、「Body」プロパティ値のみに関係します。私は次のようないくつかの失敗した試みをしました:

 jsawk -a 'return this.Body' 

または

 awk -v k="Body" '{n=split($0,a,","); for (i=1; i<=n; i++) print a[i]} 

しかし、それでは十分ではありませんでした。誰でもこれで私を助けることができますか?

50
user1305398

コマンドラインでjsonを解析するためのjqがあります:

 jq '.Body'

Jqについてはこちらをご覧ください: https://stedolan.github.io/jq/

80
user1305398

tl; dr

$ cat /tmp/so.json | underscore select '.Messages .Body' 
["172.16.1.42|/home/480/1234/5-12-2013/1234.toSort"]

Javascript CLIツール

次のようなJavascript CLIツールを使用できます。

nameのすべてのaddons子を選択します。

underscore select ".addons > .name"

underscore-cliは、他の 実世界の例json:select()doc を提供します。

12
Édouard Lopez

同様に、Bash正規表現を使用します。任意のキー/値ペアを奪うことができるものとします。

key="Body"
re="\"($key)\": \"([^\"]*)\""

while read -r l; do
    if [[ $l =~ $re ]]; then
        name="${BASH_REMATCH[1]}"
        value="${BASH_REMATCH[2]}"
        echo "$name=$value"
    else
        echo "No match"
    fi
done

正規表現は、複数のスペース/タブまたは改行に一致するように調整できます。値に"が埋め込まれていると機能しません。これは実例です。いくつかの「産業用」パーサーを使用する方が良い:)

5

粗雑な方法は次のとおりです。JSONをbash変数からeval変数に変換します。

これは以下に対してのみ機能します:

  • ネストされた配列を含まないJSON
  • 信頼できるソースからのJSON(それ以外の場合、シェルスクリプトを混乱させる可能性があります。おそらく、システムに損害を与える可能性さえあります、警告されました

ええ、そうです、CPANのおかげで、Perlを使用してこの仕事をしていますが、スクリプトに直接含めるには十分に小さいため、すばやく簡単にデバッグできます。

json2bash() {
Perl -MJSON -0777 -n -E 'sub J {
my ($p,$v) = @_; my $r = ref $v;
if ($r eq "HASH") { J("${p}_$_", $v->{$_}) for keys %$v; }
elsif ($r eq "ARRAY") { $n = 0; J("$p"."[".$n++."]", $_) foreach @$v; }
else { $v =~ '"s/'/'\\\\''/g"'; $p =~ s/^([^[]*)\[([0-9]*)\](.+)$/$1$3\[$2\]/;
$p =~ tr/-/_/; $p =~ tr/A-Za-z0-9_[]//cd; say "$p='\''$v'\'';"; }
}; J("json", decode_json($_));'
}

eval "$(json2bash <<<'{"a":["b","c"]}')"のように使用します

ただし、十分にテストされていません。更新、警告、その他の例については、私の Gist を参照してください。

更新

(残念なことに、Cコードは長すぎてここで複製できないため、以下はリンクのみのソリューションです。)

上記のソリューションが気に入らないすべての人のために、JSONをシェル変数に(できれば安全に)変換する Cプログラムjson2sh があります。 Perlスニペットとは対照的に、整形式である限り、任意のJSONを処理できます。

警告:

  • json2shはあまりテストされていません。
  • json2shは、Shellshockパターンで始まる変数を作成できます() {

json2shをシェルで後処理できるように.bsonを作成しました。

bson2json()
{
printf '[';
{ bsondump "$1"; echo "\"END$?\""; } | sed '/^{/s/$/,/';
echo ']';
};

bsons2json()
{
printf '{';
c='';
for a;
do
  printf '%s"%q":' "$c" "$a";
  c=',';
  bson2json "$a";
done;
echo '}';
};

bsons2json */*.bson | json2sh | ..

説明:

  • bson2jsonは、レコードがJSON配列になるように.bsonファイルをダンプします
    • すべてが正常に機能する場合は、END0- Markerが適用され、そうでない場合はEND1のようなものが表示されます。
    • END- Markerが必要です。そうでない場合、空の.bsonファイルは表示されません。
  • bsons2jsonは、.bsonファイルの束をオブジェクトとしてダンプします。ここで、bson2jsonの出力はファイル名でインデックス付けされます。

これはjson2shによって後処理され、grep/source/eval/etcを使用できます。シェルに値を取り込むために必要なもの。

この方法により、MongoDBに最初にインポートする必要なく、シェルレベルでMongoDBダンプの内容をすばやく処理できます。

1
Tino