Unixツールを使用したJSONの解析


879

次のように、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)を印刷するにはどうすればよいですか?


5
ええと、それはjsonの構文解析に適さない...文字列のエスケープ文字はどうですか...など、これに対するpythonの回答はSOにありますか(perlの回答も...)?
martinr 2009

51
誰かが「問題Xは他の言語Yで簡単に解決できる」と言うときはいつでも、「私のツールボックスには釘を打つための岩石しかない...なぜ他のことを気にするのか?」
BryanH 2013

22
@BryanH:Y を提案した人が知っている言語の数に関係なく、言語Y 特定の問題Xを解決するために装備できる場合があることを除いて。
jfs 2013年

15
ちょっと遅れましたが、ここに行きます。grep -Po '"'"version"'"\s*:\s*"\K([^"]*)' package.json。これは、grepを使用するだけで簡単にタスクを解決し、単純なJSONに対して完全に機能します。複雑なJSONの場合は、適切なパーサーを使用する必要があります。
diosney 2014年

2
@auser、タイトルの "with sed and awk"を "with UNIX tools"に変更しても大丈夫ですか?
Charles Duffy

回答:


1127

コマンドラインからJSONを操作する目的で特別に設計されたツールがいくつかあり、Awkを使用するよりもはるかに簡単で信頼性が高くなりますjq

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

また、Pythonが使用するように、可能性の高いシステムにすでにインストールされているツールでこれを行うことができjson、モジュールを、そしてので、まだ適切なJSONパーサーの利点を持ちながら、余分な依存を避けます。以下は、UTF-8を使用することを想定しています。UTF-8は、元のJSONをエンコードする必要があり、最新の端末でも使用されます。

Python 3:

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']"

歴史ノート

この回答はもともとjsawkを推奨しましたが、これはまだ機能するはずですが、使用するのが少し面倒jqであり、インストールされているスタンドアロンのJavaScriptインタープリターに依存します。

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

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

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

7
@thrau +1。jqはリポジトリで利用でき、非常に使いやすいので、jsawkよりもはるかに優れています。私は両方を数分間テストし、jqがこの戦いに勝ちました
SzymonSadłoJun

1
Python 2では、出力を別のコマンドにパイプする場合、Pythonをパイプで使用しているため、printステートメントは常に ASCIIにエンコードされます。PYTHONIOENCODING=<desired codec>コマンドに挿入して、端末に適した別の出力エンコーディングを設定します。Python 3では、この場合のデフォルトはUTF-8です(print() 関数を使用)。
Martijn Pieters

1
brew install
Andy Fraley

1
curl -sはと同等ですがcurl --silent、文字列引用符なしをjq -r意味jq --raw-outputします。
Serge Stroobandt

python -c "リクエストのインポート; r = requests.get( ' api.github.com/users/lambda');print r.json()[' name '];" 。一番シンプル!
NotTooTechy

277

特定のキーの値をすばやく抽出するために、私は正規表現の一致のみを返す「grep -o」を個人的に使用したいと思っています。たとえば、ツイートから「テキスト」フィールドを取得するには、次のようにします。

grep -Po '"text":.*?[^\\]",' tweets.json

この正規表現は、想像以上に堅牢です。たとえば、コンマとエスケープされた引用符が埋め込まれた文字列は問題なく処理されます。もう少し作業をすれば、アトミックなものであれば、実際に値を抽出することが保証されているものを作成できると思います。(ネストがある場合、正規表現はもちろんできません。)

さらにクリーンアップするには(文字列の元のエスケープを維持しながら)、次のようなものを使用できます| perl -pe 's/"text"://; s/^"//; s/",$//'。(私はこの分析のためにこれを行いました。)

主張するすべての嫌いな人には、実際のJSONパーサーを使用する必要があります-はい、それは正確さのために不可欠ですが、

  1. 値をカウントしてデータクリーニングのバグをチェックしたり、データの一般的な感触を得たりするなど、本当に迅速な分析を行うには、コマンドラインで何かを叩くのがより高速です。エディターを開いてスクリプトを書くのは面倒です。
  2. grep -ojson少なくともつぶやき(これはそれぞれ約2 KB)に対してこれを行う場合、Python標準ライブラリよりも桁違いに高速です。これがjson遅いだけの理由かどうかはわかりません(いつかyajlと比較する必要があります)。しかし、原則として、再帰をサポートする必要のあるパーサーの代わりに、有限状態であり、はるかに最適化できる正規表現の方が高速である必要があります。この場合、気にしない構造のツリーを構築するために大量のCPUを費やします。(誰かが適切な(深さ制限された)JSON解析を行う有限状態トランスデューサーを作成した場合、それは素晴らしいことです!それまでの間、「grep -o」を使用します。)

保守可能なコードを書くために、私は常に実際の構文解析ライブラリーを使用します。私はjsawkを試していませんが、うまく機能すればポイント1に対処できます。

最後に、奇妙な解決策として、Pythonを使用しjsonて必要なキーをタブ区切りの列に抽出するスクリプトを作成しました。次にawk、名前付きの列へのアクセスを可能にするラッパーを介してパイプします。 ここに:json2tsvおよびtsvawkスクリプト。したがって、この例では次のようになります。

json2tsv id text < tweets.json | tsvawk '{print "tweet " $id " is: " $text}'

このアプローチは#2に対応しておらず、単一のPythonスクリプトよりも非効率的であり、少しもろいです。それは、文字列値の改行とタブの正規化を強制し、awkのフィールド/レコード区切りの世界のビューでうまく機能するようにします。しかし、それよりも正確で、コマンドラインにとどまることができますgrep -o


11
整数値を忘れました。grep -Po '"text":(\d*?,|.*?[^\\]",)'
Robert

3
ロバート:そうです、私の正規表現は、そのフィールドの文字列値に対してのみ記述されていました。あなたが言うように整数を追加することができます。すべてのタイプが必要な場合は、ブール値、nullを次々と実行する必要があります。そして、配列とオブジェクトはより多くの作業を必要とします。標準の正規表現では、深さ制限のみが可能です。
ブレンダンOConnor 2013

9
1. jq .nameコマンドラインで動作し、「スクリプトを作成するためにエディターを開く」必要はありません。2.あなたの正規表現を生成することができますどのくらいの速重要ではありません、間違った結果を
JFS

6
値だけが必要な場合は、awkをスローするだけです。| grep -Po '"text":.*?[^\\]",'|awk -F':' '{print $2}'
JeffCharter 2015

34
OSXでは-Pオプションがないようです。私はOSX 10.11.5でテストしましgrep --versiongrep (BSD grep) 2.5.1-FreeBSD。OSXの「拡張正規表現」オプションを使用して動作させました。上からのコマンドはになりますgrep -Eo '"text":.*?[^\\]",' tweets.json
Jens

174

ここのいくつかの推奨事項(コメントのesp)がPythonの使用を示唆していることに基づいて、例を見つけられなかったことに失望しました。

そこで、JSONデータから単一の値を取得するための1つのライナーがあります。(どこかから)データをパイプ処理していることを想定しているため、スクリプトのコンテキストで役立つはずです。

echo '{"hostname":"test","domainname":"example.com"}' | python -c 'import json,sys;obj=json.load(sys.stdin);print obj["hostname"]'

以下の回答を拡張して、bash関数を使用するようにしました:curl 'some_api' | getJsonVal 'key'
Joe Heyming

pythonpygithub.com/russell91/pythonpypython -c、pipでインストールする必要がありますが、ほとんどの場合に代わる優れた方法です。jsonをにパイプするだけpy --ji -x 'x[0]["hostname"]'です。組み込みのjson_inputサポートを使用したくない場合でも、これらは次のように自動的にインポートされますpy 'json.loads(sys.stdin)[0]["hostname"]'
RussellStewart

2
ありがとう!もっと速くて汚いJSON解析のjsonq() { python -c "import sys,json; obj=json.load(sys.stdin); print($1)"; }ために、私はそれをbash関数にラップしました:次のように書くことができます:curl ...... | jsonq 'json.dumps([key["token"] for key in obj], indent=2)'&同様の恐ろしいものの多く...もう、obj[0]不要なようobjですが、デフォルトのケースでは問題なく動作するように見えます(?)
akavel 2015年

ありがとう。このリスペクトJSONを印刷よりも少し良くしました:jsonq() { python -c "import sys,json; obj=json.load(sys.stdin); sys.stdout.write(json.dumps($1))"; }
Adam K Dean

4
obj[0]解析時にエラーが発生します{ "port":5555 }。取り外し[0]た後は正常に動作します。
Cyber​​Ed

134

MartinRとBoeckoのリードに続いて:

$ curl -s 'http://twitter.com/users/username.json' | python -mjson.tool

それはあなたに非常にgrepフレンドリーな出力を与えるでしょう。とても便利:

$ curl -s 'http://twitter.com/users/username.json' | python -mjson.tool | grep my_key

37
OPが求めているように、特定のキーをどのように抽出しますか?
2013年

2
これまでのところ、imho、ほとんどのディストリビューションに他に何もインストールする必要はありません| grep field。できます。ありがとう!
Andrea Richiardi

7
私が間違っていなければ、これはすべてJSONをフォーマットすることです。xpathソリューションや「JSONポインター」に基づくもののように、呼び出し元が出力から特定のフィールドを選択することはできません。
Cheeso

4
最終的にはキーと値のペアができますが、値自体はありません。
クリストファー2017

1
jqPythonのインストール中には通常インストールされません。また、Pythonに移行したら、次の方法で全体を解析することもできますimport json...
CpILL

125

あなたのプラットフォーム用のバイナリをダウンロードjqして実行するだけです(chmod +x jq):

$ curl 'https://twitter.com/users/username.json' | ./jq -r '.name'

"name"jsonオブジェクトから属性を抽出します。

jqホームページsedJSONデータのようだと言っています。


27
参考までに、これjqはすばらしいツールです。
ホス2013年

2
同意した。私はそれを使用していないため、受け入れられた回答からのjsawkと比較することはできませんが、ローカルの実験(ツールのインストールが受け入れられる場合)のために、jqを強くお勧めします。ここで、配列の各要素を取得し、選択されたデータを使用して新しいJSONオブジェクトを合成やや広範例えば、次のとおり curl -s https://api.example.com/jobs | jq '.jobs[] | {id, o: .owner.username, dateCreated, s: .status.state}'
jbyler

2
これが大好き。非常に軽量で、プレーンな古いCなので、どこでもコンパイルできます。
Benmj 2014年

1
最も実用的なもの:(jsawkは必要ですが)サードパーティのライブラリを必要とせず、簡単にインストールできます(OSX:brew install jq)
lauhub

1
これは、私のユースケースで最も実用的で簡単に実装できる回答です。Ubuntu(14.04)システムの場合、単純なapt-get install jqがシステムにツールを追加しました。AWS CLI応答からのJSON出力をjqにパイプ処理していますが、応答にネストされている特定のキーに値を抽出するのに最適です。
Brandon K

105

Node.jsの使用

システムが持っている場合 インストールされている場合、-pprintおよび-eevaulateスクリプトフラグを使用して、JSON.parse必要なすべての値を引き出すことができます。

JSON文字列を使用して{ "foo": "bar" }"foo"の値を取得する簡単な例:

$ node -pe 'JSON.parse(process.argv[1]).foo' '{ "foo": "bar" }'
bar

catおよびその他のユーティリティにアクセスできるため、これをファイルに使用できます。

$ node -pe 'JSON.parse(process.argv[1]).foo' "$(cat foobar.json)"
bar

または、JSONを含むURLなどの他の形式:

$ node -pe 'JSON.parse(process.argv[1]).name' "$(curl -s https://api.github.com/users/trevorsenior)"
Trevor Senior

1
ありがとう!しかし私の場合、それは-eフラグでのみ機能しますnode -p -e 'JSON.parse(process.argv[1]).foo' '{ "foo": "bar" }'
Rnd_d

33
パイプ!curl -s https://api.github.com/users/trevorsenior | node -pe "JSON.parse(require('fs').readFileSync('/dev/stdin').toString()).name"
nicerobot 2014年

4
これが私のお気に入りのソリューションです。言語(JavaScript)を使用して、自然なデータ構造(JSON)を解析します。最も正しいようです。また、ノードはおそらくシステムですでに使用可能であり、jqのバイナリ(別の正しい選択のように見えます)を壊す必要はありません。
Eliran Malka 2017

これはbashスクリプト関数です:#jsonv特定の属性のjsonオブジェクト値を取得#最初のパラメーターはjsonドキュメント#2番目のパラメーターは返される属性get_json_attribute_value(){node -pe 'JSON.parse(process。 argv [1])[process.argv [2]] '"$ 1" "$ 2"}
Youness

6
Node.jsの10で、次の作品:cat package.json | node -pe 'JSON.parse(fs.readFileSync(0)).version'
イリヤBoyandin

100

awkを使用する代わりにPythonのJSONサポートを使用してください!

このようなもの:

curl -s http://twitter.com/users/username.json | \
    python -c "import json,sys;obj=json.load(sys.stdin);print obj['name'];"

6
良い応答を考え出そうとしたことに対して私を許してください...:私はもっと一生懸命頑張ります。党派性は、それを振り払うためにawkスクリプトを書くだけではありません!
martinr 2009

9
なぜそのonelinerソリューションでobj変数を使用するのですか?それは役に立たず、とにかく保存されていませんか?あなたは使用して以下の書き込みjson.load(sys.stdin)['"key']"のような例として:curl -sL httpbin.org/ip | python -c "import json,sys; print json.load(sys.stdin)['origin']"
m3nda 2016

65

あなたは足で自分を撃つ方法を尋ねました、そして私は弾薬を提供するためにここにいます:

curl -s 'http://twitter.com/users/username.json' | sed -e 's/[{}]/''/g' | awk -v RS=',"' -F: '/^text/ {print $2}'

tr -d '{}'代わりに使用できますsed。しかし、それらを完全に除外すると、望ましい効果も得られるようです。

外側の引用符を取り除きたい場合は、上記の結果をパイプして sed 's/\(^"\|"$\)//g'

他の人は十分な警報を鳴らしたと思います。携帯電話で救急車を呼んで待機します。準備ができたら発砲します。



3
私はすべての回答を読みましたが、これは余分な依存関係がなくても完璧に機能します。+1
eth0

それが私が探していたものです。唯一の修正-引用符を削除するために提供されたsedコマンドが機能しませんでした。代わりにsed 's / "// g'を使用しました
AlexG

44

PythonでのBashの使用

.bash_rcファイルにbash関数を作成します

function getJsonVal () { 
    python -c "import json,sys;sys.stdout.write(json.dumps(json.load(sys.stdin)$1))"; 
}

その後

$ curl 'http://twitter.com/users/username.json' | getJsonVal "['text']"
My status
$ 

これは同じ機能ですが、エラーチェックを備えています。

function getJsonVal() {
   if [ \( $# -ne 1 \) -o \( -t 0 \) ]; then
       cat <<EOF
Usage: getJsonVal 'key' < /tmp/
 -- or -- 
 cat /tmp/input | getJsonVal 'key'
EOF
       return;
   fi;
   python -c "import json,sys;sys.stdout.write(json.dumps(json.load(sys.stdin)$1))";
}

$#-ne 1は少なくとも1つの入力を確認し、-t 0はパイプからリダイレクトしていることを確認します。

この実装の良い点は、ネストされたjson値にアクセスして、jsonを取得できることです。=)

例:

$ echo '{"foo": {"bar": "baz", "a": [1,2,3]}}' |  getJsonVal "['foo']['a'][1]"
2

本当に凝りたいなら、データをきれいに印刷できます:

function getJsonVal () { 
    python -c "import json,sys;sys.stdout.write(json.dumps(json.load(sys.stdin)$1, sort_keys=True, indent=4))"; 
}

$ echo '{"foo": {"bar": "baz", "a": [1,2,3]}}' |  getJsonVal "['foo']"
{
    "a": [
        1, 
        2, 
        3
    ], 
    "bar": "baz"
}

bash機能なしのワンライナー:curl http://foo | python -c 'import json,sys;obj=json.load(sys.stdin);print obj["environment"][0]["name"]'
Cheeso

1
sys.stdout.write()python 2と3の両方で動作させたい場合
ヨハンソン氏による

system.stdout.write(obj $ 1)に変更する必要があると思います。そのように言うことができます:@Cheesoの例のようなgetJsonVal "['environment'] ['name']"
Joe Heyming

1
@Narekその場合、それは次のようになりますgetJsonVal() { py -x "json.dumps(json.loads(x)$1, sort_keys=True, indent=4)"; }
。function

30

TickTickはbashで記述されたJSONパーサーです(<250行のコード)

これが彼の記事からの作者の抜粋です、BashがJSONをサポートする世界を想像してください

#!/bin/bash
. ticktick.sh

``  
  people = { 
    "Writers": [
      "Rod Serling",
      "Charles Beaumont",
      "Richard Matheson"
    ],  
    "Cast": {
      "Rod Serling": { "Episodes": 156 },
      "Martin Landau": { "Episodes": 2 },
      "William Shatner": { "Episodes": 2 } 
    }   
  }   
``  

function printDirectors() {
  echo "  The ``people.Directors.length()`` Directors are:"

  for director in ``people.Directors.items()``; do
    printf "    - %s\n" ${!director}
  done
}   

`` people.Directors = [ "John Brahm", "Douglas Heyes" ] ``
printDirectors

newDirector="Lamont Johnson"
`` people.Directors.push($newDirector) ``
printDirectors

echo "Shifted: "``people.Directors.shift()``
printDirectors

echo "Popped: "``people.Directors.pop()``
printDirectors

2
ここで唯一の堅牢なピュアバッシュの回答として、これはより多くの賛成票を投じる価値があります。
Ed Randall

このピープル変数をjson文字列にもう一度出力する方法はありますか?それは非常に役立つでしょう
Thomas Fournet

1
最後に、Pythonやその他の凶悪な方法を推奨しない回答...ありがとうございます!
アキト

21

PHP CLIを使用したJSONの解析

間違いなく話題から外れていますが、私たちの信頼できる忠実なPHPについて言及することなく、優先順位がこの質問を支配しているため、私は正しいですか?

同じJSONの例を使用しますが、それを変数に割り当てて、あいまいさを減らします。

$ export JSON='{"hostname":"test","domainname":"example.com"}'

次に、PHPの良さのために、file_get_contentsおよびphp:// stdinストリームラッパーを使用します。

$ echo $JSON|php -r 'echo json_decode(file_get_contents("php://stdin"))->hostname;'

または、fgetsを使用して指摘され、CLI定数STDINですでに開かれているストリーム。

$ echo $JSON|php -r 'echo json_decode(fgets(STDIN))->hostname;'

nJoy!


$argn代わりに使用することもできますfgets(STDIN)
IcanDivideBy0

おっと、$argn-Eまたは-Rフラグで機能し、JSONコンテンツが1行にある場合にのみ機能します...
IcanDivideBy0

21

ネイティブBashバージョン:バックスラッシュ(\)および引用符( ")でも適切に機能します。

function parse_json()
{
    echo $1 | \
    sed -e 's/[{}]/''/g' | \
    sed -e 's/", "/'\",\"'/g' | \
    sed -e 's/" ,"/'\",\"'/g' | \
    sed -e 's/" , "/'\",\"'/g' | \
    sed -e 's/","/'\"---SEPERATOR---\"'/g' | \
    awk -F=':' -v RS='---SEPERATOR---' "\$1~/\"$2\"/ {print}" | \
    sed -e "s/\"$2\"://" | \
    tr -d "\n\t" | \
    sed -e 's/\\"/"/g' | \
    sed -e 's/\\\\/\\/g' | \
    sed -e 's/^[ \t]*//g' | \
    sed -e 's/^"//'  -e 's/"$//'
}


parse_json '{"username":"john, doe","email":"john@doe.com"}' username
parse_json '{"username":"john doe","email":"john@doe.com"}' email

--- outputs ---

john, doe
johh@doe.com

これは素晴らしいです。ただし、JSON文字列に複数のメールキーが含まれている場合、パーサーはjohn@doe.com "" john@doe.com
rtc11

jean-pierre@email.comのようにダッシュがメールに含まれている場合は機能しません
alexmngn

13

Rubyとhttp://flori.github.com/json/を使用するバージョン

$ < file.json ruby -e "require 'rubygems'; require 'json'; puts JSON.pretty_generate(JSON[STDIN.read]);"

より簡潔に:

$ < file.json ruby -r rubygems -r json -e "puts JSON.pretty_generate(JSON[STDIN.read]);"

3
これは私のお気に入りです;)ところで、ruby -rjsonを使用してライブラリを必要とするようにショートさせることができます
lucapette

;Ruby ではfinal は必要ないことに注意してください(通常は別々の行にあるステートメントを1行に連結するためにのみ使用されます)。
ザックモリス

11

残念ながら、使用した上位投票の回答は、私のシナリオでは機能しなかっgrep完全一致を返しますが、JSON形式が一定のままであることがわかっている場合は、後読み先読みを使用して目的の値のみを抽出できます。

# echo '{"TotalPages":33,"FooBar":"he\"llo","anotherValue":100}' | grep -Po '(?<="FooBar":")(.*?)(?=",)'
he\"llo
# echo '{"TotalPages":33,"FooBar":"he\"llo","anotherValue":100}' | grep -Po '(?<="TotalPages":)(.*?)(?=,)'
33
#  echo '{"TotalPages":33,"FooBar":"he\"llo","anotherValue":100}' | grep -Po '(?<="anotherValue":)(.*?)(?=})'
100

JSON辞書の要素の順序を実際に知ることはありません。それらは、定義上、順序付けされていません。これがまさに、独自のJSONパーサーをロールすることが運命のアプローチである根本的な理由の1つです。
Tripleee

10

誰かがネストされた構造を必要とせずに単純なJSONオブジェクトから値を抽出したいだけの場合は、bashを離れることなく正規表現を使用できます。

JSON標準に基づくbash正規表現を使用して定義した関数を次に示します

function json_extract() {
  local key=$1
  local json=$2

  local string_regex='"([^"\]|\\.)*"'
  local number_regex='-?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][+-]?[0-9]+)?'
  local value_regex="${string_regex}|${number_regex}|true|false|null"
  local pair_regex="\"${key}\"[[:space:]]*:[[:space:]]*(${value_regex})"

  if [[ ${json} =~ ${pair_regex} ]]; then
    echo $(sed 's/^"\|"$//g' <<< "${BASH_REMATCH[1]}")
  else
    return 1
  fi
}

警告:オブジェクトと配列は値としてサポートされていませんが、標準で定義されている他のすべての値タイプはサポートされています。また、ペアが一致するのは、JSONドキュメントの深さがどれであっても、完全に同じキー名である限りです。

OPの例を使用:

$ json_extract text "$(curl 'http://twitter.com/users/username.json')"
My status

$ json_extract friends_count "$(curl 'http://twitter.com/users/username.json')"
245

Helder Pereiraでは、この関数を使用してネストされたプロパティ値を抽出できますか?
vsbehere

8

json文字列からプロパティを取得する簡単な方法があります。package.json例としてファイルを使用して、これを試してください:

#!/usr/bin/env bash
my_val="$(json=$(<package.json) node -pe "JSON.parse(process.env.json)['version']")"

process.envこれは、悪意のあるコンテンツが引用をエスケープしてコードとして解析されるリスクなしに、ファイルのコンテンツを文字列としてnode.jsに取得するために使用しています。


文字列連結を使用して、コードとして解析された文字列に値を代入すると、任意のnode.jsコードが実行される可能性があります。つまり、インターネットから取得したランダムなコンテンツで使用することは非常に危険です。JavaScriptでJSONを解析するための安全でベストプラクティスな方法がそれを評価するだけではないのには、理由があります。
Charles Duffy

@CharlesDuffy私がフォローしていることはわかりませんが、JSON.parse呼び出しはrequire()実際に外部コードを実行できるため、JSON.parse呼び出しはより安全であるはずです。
Alexander Mills

パーサーをバイパスするような方法で文字列が実際にJSONランタイムに注入された場合、それは真実です。ここのコードがそれを確実に実行しているとは思いません。それを環境変数からプルして渡してJSON.parse()、はい、あなたは明白に安全です...しかし、ここでは、JSONランタイムが(信頼されていない)コンテンツを(信頼された)コードとインバンドで受け取っています。
Charles Duffy

...同様に、コードでファイルからJSONを文字列として読み取り、その文字をに渡すJSON.parse()場合も、安全ですが、ここでも発生しません。
Charles Duffy

1
...ああ、まあ、すぐに「どうやって」に入るかもしれません。問題は、渡されるシェル変数をJSON.parse()コードに代入していることです。あなたしているが、仮定リテラルバッククォートは、ファイルの内容(したがって変数)に存在することができるので、文字通りのバッククォートを置くことは、文字通りの内容を保持し、それは完全に安全ではないという仮定だということ、したがって、引用を終了し、どこ引用符で囲まれていないコンテキストを入力することができます値はコードとして実行されます。
Charles Duffy

7

Powershellがクロスプラットフォームになったので、かなり直感的で非常にシンプルであることがわかったので、私はそれをそこに捨てると思いました。

curl -s 'https://api.github.com/users/lambda' | ConvertFrom-Json 

ConvertFrom-Jsonは、JSONをPowershellカスタムオブジェクトに変換するため、その時点からプロパティを簡単に操作できます。たとえば、 'id'プロパティのみが必要な場合は、次のようにします。

curl -s 'https://api.github.com/users/lambda' | ConvertFrom-Json | select -ExpandProperty id

すべてをBash内から呼び出したい場合は、次のように呼び出す必要があります。

powershell 'curl -s "https://api.github.com/users/lambda" | ConvertFrom-Json'

もちろん、カールせずにそれを行う純粋なPowershellの方法があります。

Invoke-WebRequest 'https://api.github.com/users/lambda' | select -ExpandProperty Content | ConvertFrom-Json

最後に、カスタムオブジェクトをJSONに簡単に変換する 'ConvertTo-Json'もあります。次に例を示します。

(New-Object PsObject -Property @{ Name = "Tester"; SomeList = @('one','two','three')}) | ConvertTo-Json

次のような素晴らしいJSONが生成されます。

{
"Name":  "Tester",
"SomeList":  [
                 "one",
                 "two",
                 "three"
             ]

}

確かに、UnixでWindowsシェルを使用することはやや非難的ですが、Powershellはいくつかの点で非常に優れており、JSONとXMLの解析はその2つです。これはクロスプラットフォームバージョンのGitHubページhttps://github.com/PowerShell/PowerShell


ツールをオープンソース化し、オープンソースの外部ツールを組み込むというマイクロソフトの新しい戦略を推進しているため、賛成です。それは私たちの世界にとって良いことです。
アレックス

以前はPowerShellが嫌いでしたが、オブジェクトは非常に優れているため、JSONの処理を認めなければなりません。
MartinThé

6

XMLファイルも持っている人は、私のXidelを確認したいと思うかもしれません。それは、cli、依存関係のないJSONiqプロセッサです。(つまり、xmlまたはjson処理用のXQueryもサポートします)

質問の例は次のようになります:

 xidel -e 'json("http://twitter.com/users/username.json")("name")'

または、私自身の非標準の拡張構文を使用して:

 xidel -e 'json("http://twitter.com/users/username.json").name'

1
または最近ではもっと簡単です:(xidel -s https://api.github.com/users/lambda -e 'name'または-e '$json/name'、または-e '($json).name')。
レイノ

6

ここでは答えを使用できません。利用可能なjq、シェル配列、宣言なし、grep -P、後読みなし、先読みなし、Python、Perl、Rubyなし、Bashなし...残りの答えは単にうまく機能しません。JavaScriptはおなじみのように聞こえますが、缶詰にはNescaffeと書かれています-だから、それも行く必要はありません:)利用可能であっても、私の単純な必要性のために、それらはやり過ぎで遅くなります。

しかし、モデムのjson形式の応答から多くの変数を取得することは非常に重要です。私はルーターでBusyBoxを非常にトリミングしてshでそれをやっています!awkだけを使用しても問題ありません。区切り文字を設定してデータを読み取るだけです。単一の変数の場合、それだけです!

awk 'BEGIN { FS="\""; RS="," }; { if ($2 == "login") {print $4} }' test.json

配列がないことを覚えていますか?awkで解析したデータを、シェルスクリプトで必要な11個の変数に割り当てる必要がありました。私がどこを見ても、それは不可能な任務であると言われていました。それも問題ありません。

私の解決策は簡単です。このコードは次のようになります。1)質問から.jsonファイルを解析し(実際、私は最も賛成された回答から作業データサンプルを借用しました)、引用されたデータを取り出します。さらに2)awk内からシェル変数を作成し、無料の名前付きシェルを割り当てます変数名。

eval $( curl -s 'https://api.github.com/users/lambda' | 
awk ' BEGIN { FS="\""; RS="," };
{
    if ($2 == "login") { print "Login=\""$4"\"" }
    if ($2 == "name") { print "Name=\""$4"\"" }
    if ($2 == "updated_at") { print "Updated=\""$4"\"" }
}' )
echo "$Login, $Name, $Updated"

空白の問題はありません。私の使用では、同じコマンドが長い1行の出力を解析します。evalが使用されるため、このソリューションは信頼できるデータのみに適しています。それを引用符で囲まれていないデータのピックアップに適合させるのは簡単です。非常に多くの変数の場合、else ifを使用して限界速度の向上を達成できます。配列の欠如は、明らかに、余分な操作なしに複数のレコードがないことを意味します。しかし、アレイが利用可能な場合、このソリューションの適応は簡単な作業です。

@maikel sed回答はほぼ機能します(ただし、コメントはできません)。私のきれいにフォーマットされたデータのために-それは動作します。ここで使用されている例ではそれほど多くありません(引用符がないとそれが失われます)。変更が複雑で難しい。さらに、11個の変数を抽出するために11回の呼び出しを行う必要がありません。どうして?9つの変数を抽出する100ループの時間を計った:sed関数は48.99秒かかり、私のソリューションは0.91秒かかりました!公平ではない?9つの変数を1回抽出するだけです:0.51対0.02秒。


5

あなたはこのようなものを試すことができます-

curl -s 'http://twitter.com/users/jaypalsingh.json' | 
awk -F=":" -v RS="," '$1~/"text"/ {print}'

5

使用できますjshon

curl 'http://twitter.com/users/username.json' | jshon -e text

このサイトによると、「2倍の速さで、メモリの6分の1」...そして「JshonはJSONを解析、読み取り、作成します。これは、シェル内から可能な限り使用できるように設計されており、脆弱なアドホックパーサーをgrep / sed / awk、およびperl / pythonから作成されたヘビー級の1行パーサー。 "
Roger

これは、BashでJSONを解析するための推奨ソリューションとしてリストされています
qodeninja 2017

結果の周りの引用符を取り除く最も簡単な方法は何ですか?
gMale

4

これがawkでできる1つの方法です

curl -sL 'http://twitter.com/users/username.json' | awk -F"," -v k="text" '{
    gsub(/{|}/,"")
    for(i=1;i<=NF;i++){
        if ( $i ~ k ){
            print $i
        }
    }
}'

4

より複雑なJSON解析については、python jsonpathモ​​ジュール(Stefan Goessnerによる)の使用をお勧めします-

  1. インストールする-

sudo easy_install -U jsonpath

  1. これを使って -

file.jsonの例(http://goessner.net/articles/JsonPathから)-

{ "store": {
    "book": [ 
      { "category": "reference",
        "author": "Nigel Rees",
        "title": "Sayings of the Century",
        "price": 8.95
      },
      { "category": "fiction",
        "author": "Evelyn Waugh",
        "title": "Sword of Honour",
        "price": 12.99
      },
      { "category": "fiction",
        "author": "Herman Melville",
        "title": "Moby Dick",
        "isbn": "0-553-21311-3",
        "price": 8.99
      },
      { "category": "fiction",
        "author": "J. R. R. Tolkien",
        "title": "The Lord of the Rings",
        "isbn": "0-395-19395-8",
        "price": 22.99
      }
    ],
    "bicycle": {
      "color": "red",
      "price": 19.95
    }
  }
}

解析する(価格<10のすべての本のタイトルを抽出)-

$ cat file.json | python -c "import sys, json, jsonpath; print '\n'.join(jsonpath.jsonpath(json.load(sys.stdin), 'store.book[?(@.price < 10)].title'))"

出力されます-

Sayings of the Century
Moby Dick

注:上記のコマンドラインにはエラーチェックが含まれていません。エラーチェックを伴う完全なソリューションの場合、小さなPythonスクリプトを作成し、try-exceptでコードをラップする必要があります。


美しいイディオム。私もPythonを知りませんが、これは強力な解決策のようです
Sridhar Sarnobat '20

私は少し困っインストール持っていたjsonpathので、インストールjsonpath_rw1):ので、ここで、代わりに上記の作業をしない場合、あなたが試すことができます似たものです/usr/bin/python -m pip install jsonpath-rw)2 cat ~/trash/file.json | /usr/bin/python -c "from jsonpath_rw import jsonpath, parse; import sys,json; jsonpath_expr = parse('store.book[0]'); out = [match.value for match in jsonpath_expr.find(json.load(sys.stdin))]; print out;"、私は複数のニシキヘビでいくつかの問題を抱えていたので、(私はPythonのバイナリへのフルパスを使用しましたインストール済み)。
Sridhar Sarnobat

4

あなたがphpを持っているなら:

php -r 'var_export(json_decode(`curl http://twitter.com/users/username.json`, 1));'

例:
jsonに国のISOコードを提供するリソースがあります:http : //country.io/iso3.jsonとcurlを使用してシェルで簡単に確認できます。

curl http://country.io/iso3.json

しかし、それはあまり便利ではなく、読みにくいように見えます。jsonを解析し、読みやすい構造を参照してください。

php -r 'var_export(json_decode(`curl http://country.io/iso3.json`, 1));'

このコードは次のようなものを出力します:

array (
  'BD' => 'BGD',
  'BE' => 'BEL',
  'BF' => 'BFA',
  'BG' => 'BGR',
  'BA' => 'BIH',
  'BB' => 'BRB',
  'WF' => 'WLF',
  'BL' => 'BLM',
  ...

ネストされた配列がある場合、この出力ははるかに良く見えます...

これが役に立てば幸い...


4

非常にシンプルだが強力なJSON CLI処理ツールfxhttps://github.com/antonmedv/fx

BashターミナルでのJSONフォーマットの例

無名関数を使用します。

$ echo '{"key": "value"}' | fx "x => x.key"
value

匿名関数param => ...を渡さない場合、コードは自動的に匿名関数に変換されます。そして、次のキーワードでJSONにアクセスできます。

$ echo '[1,2,3]' | fx "this.map(x => x * 2)"
[2, 4, 6]

または、ドット構文も使用します。

$ echo '{"items": {"one": 1}}' | fx .items.one
1

JSONを削減するために、任意の数の匿名関数を渡すことができます。

$ echo '{"items": ["one", "two"]}' | fx "this.items" "this[1]"
two

スプレッド演算子を使用して既存のJSONを更新できます。

$ echo '{"count": 0}' | fx "{...this, count: 1}"
{"count": 1}

単なるJavaScriptです。新しい構文を学ぶ必要はありません。


2018年11月6日更新

fxインタラクティブモード(

https://github.com/antonmedv/fx


7
自分の作品を宣伝する場合は、それを明示する必要があります。スパマーにならないようにする方法を
tripleee 2018年

4

これはまた別ですbashpythonハイブリッド答え。より複雑なJSON出力を処理したいので、この回答を投稿しましたが、bashアプリケーションの複雑さは軽減されました。http://www.arcgis.com/sharing/rest/info?f=json in から次のJSONオブジェクトをクラックして開きますbash

{
  "owningSystemUrl": "http://www.arcgis.com",
  "authInfo": {
    "tokenServicesUrl": "https://www.arcgis.com/sharing/rest/generateToken",
    "isTokenBasedSecurity": true
  }
}

次の例ではjq、をunquote利用して独自の実装を作成しましたpython。PythonオブジェクトをjsonPythonディクショナリにインポートすると、Python構文を使用してディクショナリをナビゲートできるようになります。上記をナビゲートするための構文は次のとおりです。

  • data
  • data[ "authInfo" ]
  • data[ "authInfo" ][ "tokenServicesUrl" ]

bashでマジックを使用することdataで、Pythonテキストを省略して、データの右側にのみ提供します。

  • jq
  • jq '[ "authInfo" ]'
  • jq '[ "authInfo" ][ "tokenServicesUrl" ]'

パラメータなしの場合jq、JSONプリティファイアとして機能します。パラメーターを使用すると、Python構文を使用して、サブディクショナリーや配列要素のナビゲートなど、ディクショナリーから必要なものをすべて抽出できます。

上記を実証する実用的な例を次に示します。

jq_py() {
cat <<EOF
import json, sys
data = json.load( sys.stdin )
print( json.dumps( data$1, indent = 4 ) )
EOF
}

jq() {
  python -c "$( jq_py "$1" )"
}

unquote_py() {
cat <<EOF
import json,sys
print( json.load( sys.stdin ) )
EOF
}

unquote() {
  python -c "$( unquote_py )"
}

curl http://www.arcgis.com/sharing/rest/info?f=json | tee arcgis.json
# {"owningSystemUrl":"https://www.arcgis.com","authInfo":{"tokenServicesUrl":"https://www.arcgis.com/sharing/rest/generateToken","isTokenBasedSecurity":true}}

cat arcgis.json | jq
# {
#     "owningSystemUrl": "https://www.arcgis.com",
#     "authInfo": {
#         "tokenServicesUrl": "https://www.arcgis.com/sharing/rest/generateToken",
#         "isTokenBasedSecurity": true
#     }
# }

cat arcgis.json | jq '[ "authInfo" ]'
# {
#     "tokenServicesUrl": "https://www.arcgis.com/sharing/rest/generateToken",
#     "isTokenBasedSecurity": true
# }

cat arcgis.json | jq '[ "authInfo" ][ "tokenServicesUrl" ]'
# "https://www.arcgis.com/sharing/rest/generateToken"

cat arcgis.json | jq '[ "authInfo" ][ "tokenServicesUrl" ]' | unquote
# https://www.arcgis.com/sharing/rest/generateToken

3

次のようにして、特定の値のjson応答を「解析」しました。

curl $url | grep $var | awk '{print $2}' | sed s/\"//g 

明らかに、ここの$ urlはTwitterのURLであり、$ varはそのvarの応答を取得するための「テキスト」です。

本当に、私がOPを実行している唯一のことは、彼が求めている特定の変数を含む行のgrepだけです。Awkは行の2番目の項目を取得し、sedを使用して引用符を取り除きます。

私よりも賢い人なら、おそらくawkやgrepで全体を考えることができます。

これで、sedだけですべてを実行できます。

curl $url | sed '/text/!d' | sed s/\"text\"://g | sed s/\"//g | sed s/\ //g

したがって、awkもgrepもありません...なぜそのことを以前に考えなかったのかわかりません。うーん...


実際には、sedでできること
tonybaldwin

1
grep | awk | sedそしてsed | sed | sedパイプラインは無駄なアンチパターンです。最後の例は簡単に書き直すことができますcurl "$url" | sed '/text/!d;s/\"text\"://g;s/\"//g;s/\ //g'が、他の人が指摘したように、これはエラーが発生しやすく、そもそも推奨されるべきではない脆弱なアプローチです。
tripleee 2014

grep -oPz 'name \ ":\"。*?\ "' curloutput | sed 's / name \":/ \ n / g'を使用する必要がありました
Ferroao

3

JSONの解析は、シェルスクリプトでは苦痛です。より適切な言語を使用して、シェルスクリプトの規則に準拠した方法でJSON属性を抽出するツールを作成します。新しいツールを使用してシェルスクリプトの差し迫った問題を解決し、将来の状況に備えてキットに追加できます。

たとえば、jsonlookupツールについて考えます。私が言っjsonlookup access token idた場合、おそらくJSONデータであるstdinからの属性アクセス内で定義された属性トークン内で定義された属性IDを返します。属性が存在しない場合、ツールは何も返しません(終了ステータス1)。解析が失敗した場合は、ステータス2を終了し、メッセージをstderrに送信します。検索が成功した場合、ツールは属性の値を出力します。

JSON値を抽出するという正確な目的でUNIXツールを作成したら、シェルスクリプトで簡単に使用できます。

access_token=$(curl <some horrible crap> | jsonlookup access token id)

jsonlookupの実装はどの言語でも可能です。これはかなり簡潔なpythonバージョンです:

#!/usr/bin/python                                                               

import sys
import json

try: rep = json.loads(sys.stdin.read())
except:
    sys.stderr.write(sys.argv[0] + ": unable to parse JSON from stdin\n")
    sys.exit(2)
for key in sys.argv[1:]:
    if key not in rep:
        sys.exit(1)
    rep = rep[key]
print rep

3

Pythonを使用する2ライナー。単一の.shファイルを作成していて、別の.pyファイルに依存したくない場合は、特にうまくいきます。また、パイプの使用方法も活用しています|echo "{\"field\": \"value\"}"jsonをstdoutに出力するものに置き換えることができます。

echo "{\"field\": \"value\"}" | python -c 'import sys, json
print(json.load(sys.stdin)["field"])'

問題はPythonソリューションを探すことではありませんでした。コメントもご覧ください。
Andrew Barber

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.