web-dev-qa-db-ja.com

シェルスクリプトでJSON配列をループする

以下は、curlコマンドの出力(ブランチに関するファイル情報)です。ファイル名、ファイルタイプ、サイズを印刷するには、スクリプトまたはコマンドが必要です。

私はjqを試してみましたが、単一の値(jq '.values[].size'

{
  "path": {
    "components": [],
    "name": "",
    "toString": ""
  },
  "revision": "master",
  "children": {
    "size": 5,
    "limit": 500,
    "isLastPage": true,
    "values": [
      {
        "path": {
          "components": [
            ".gitignore"
          ],
          "parent": "",
          "name": ".gitignore",
          "extension": "gitignore",
          "toString": ".gitignore"
        },
        "contentId": "c9e472ef4e603480cdd85012b01bd5f4eddc86c6",
        "type": "FILE",
        "size": 224
      },
      {
        "path": {
          "components": [
            "Jenkinsfile"
          ],
          "parent": "",
          "name": "Jenkinsfile",
          "toString": "Jenkinsfile"
        },
        "contentId": "e878a88eed6b19b2eb0852c39bfd290151b865a4",
        "type": "FILE",
        "size": 1396
      },
      {
        "path": {
          "components": [
            "README.md"
          ],
          "parent": "",
          "name": "README.md",
          "extension": "md",
          "toString": "README.md"
        },
        "contentId": "05782ad495bfe11e00a77c30ea3ce17c7fa39606",
        "type": "FILE",
        "size": 237
      },
      {
        "path": {
          "components": [
            "pom.xml"
          ],
          "parent": "",
          "name": "pom.xml",
          "extension": "xml",
          "toString": "pom.xml"
        },
        "contentId": "9cd4887f8fc8c2ecc69ca08508b0f5d7b019dafd",
        "type": "FILE",
        "size": 2548
      },
      {
        "path": {
          "components": [
            "src"
          ],
          "parent": "",
          "name": "src",
          "toString": "src"
        },
        "node": "395c71003030308d1e4148b7786e9f331c269bdf",
        "type": "DIRECTORY"
      }
    ],
    "start": 0
  }
}

予想される出力は以下のようになります

.gitignore    FILE     224

Jenkinsfile   FILE     1396

質問で提供されているユースケースの場合、@ JigglyNagaの答えはおそらくこれよりも優れていますが、より複雑なタスクでは、keysを使用してリスト項目をループすることもできます。

fileから:

for k in $(jq '.children.values | keys | .[]' file); do
    ...
done

または文字列から:

for k in $(jq '.children.values | keys | .[]' <<< "$MYJSONSTRING"); do
    ...
done

したがって、たとえばあなたが使うかもしれません:

for k in $(jq '.children.values | keys | .[]' file); do
    value=$(jq -r ".children.values[$k]" file);
    name=$(jq -r '.path.name' <<< "$value");
    type=$(jq -r '.type' <<< "$value");
    size=$(jq -r '.size' <<< "$value");
    printf '%s\t%s\t%s\n' "$name" "$type" "$size";
done | column -t -s$'\t'

値の改行がない場合は、ループ内でjqを1回呼び出すだけで作成できます。

for k in $(jq '.children.values | keys | .[]' file); do
    IFS=$'\n' read -r -d '' name type size \
        <<< "$(jq -r ".children.values[$k] | .path.name,.type,.size" file)"
    printf '%s\t%s\t%s\n' "$name" "$type" "$size";
done | column -t -s$'\t'
7
pLumo

メンバーの抽出

jq -c '.children.values[]|[.path.components[0],.type,.size]'
  • .children.values[]は、配列.valuesのすべてのメンバーを出力します。
  • |は、シェルパイプのように、前の結果を次のフィルターにパイプします
  • [...,...,...]は、内部のすべての用語を1つの配列に表示します
  • -cオプションは、「コンパクト」形式、つまり1行に1つのオブジェクト

結果:

[".gitignore","FILE",224]
["Jenkinsfile","FILE",1396]
["README.md","FILE",237]
...

結果のフォーマット

きちんと配置されたテーブルを出力する場合、それは columnpaste などの他のツールでより適切に処理されるタスクです。

jq -c '.children.values[]|[.path.components[0],.type,.size]' | column -t -s'[],"'
  • -tは、入力に基づいて列の数を推測するようcolumnに指示します
  • -s...は区切り文字を指定します

結果:

.gitignore   FILE       224
Jenkinsfile  FILE       1396
README.md    FILE       237

これは、[],"の文字がファイル名に表示されていないことに依存しています。これは安全な想定ではありません。

pasteは、複数の入力を並べて配置することもできます。このために、JSON構造を完全に削除して、生の行を出力できます(@muruへのヒント)。

jq -r '.children.values[]|.path.components[0],.type,.size' | paste - - -

paste - - -は3つの列を意味し、すべて同じソースから読み取られます。今回は、ファイル名に改行が含まれていないことが唯一の前提です。

15
JigglyNaga

jqは、その出力をさまざまな形式にレンダリングできます。次を参照してください https://stedolan.github.io/jq/manual/#Formatstringsandescaping

タブ区切り出力の場合:

$ jq -r '.children.values[] | [.path.name, .type, .size] | @tsv' file.json
.gitignore  FILE    224
Jenkinsfile FILE    1396
README.md   FILE    237
pom.xml FILE    2548
src DIRECTORY   
3
glenn jackman

ramda-cliによる解決策:

% curl ... | ramda -o tsv '.children.values' 'map flat' 'map props ["path.name", "type", "size"]'
.gitignore      FILE    224
Jenkinsfile     FILE    1396
README.md       FILE    237
pom.xml FILE    2548
src     DIRECTORY

最初に値のリストに移動し、次にflatを使用してリストをマッピングし、深いオブジェクト構造である各エントリを、ドットで区切られたキーを持つ浅いものに変換します。

次に、リストを再度マップし、文字列で表されたパスに基づいて必要なプロパティを選択できます。

最後に、-o tsvは、結果のリストのリストをtsv形式に変換します。

何が起こっているのかをデバッグしたりさらに理解したりするには、コマンドの最後から引数を1つずつ削除し、各ステップでの出力の違いを観察して、各引数の機能を確認します。これらは単に、左から右に1つずつデータに適用される操作(または関数)です。

1
raine

jtcおよびxargsに基づく1つのライナーソリューション:

bash $ jtc -x'<values>l[+0]<size>l[-1]' -y'<name>l' -y'<type>l' -y'<size>l' your.json | xargs -n3
.gitignore FILE 224
Jenkinsfile FILE 1396
README.md FILE 237
pom.xml FILE 2548
bash $ 

注:ファイルにjsonの不規則性があります(すべてのレコードにサイズキーはありません)。これを除外するには、最初の引数-xを作成しますその方法(サイズが存在するレコードのみを処理します)。

1
Dmitry L.