LinuxシェルスクリプトからYAMLファイルを解析するにはどうすればよいですか?


192

私は、技術者以外のユーザーができるだけ簡単に編集できる構造化構成ファイルを提供したいので(残念ながらファイルである必要があります)、YAMLを使用したいと思いました。ただし、これをUnixシェルスクリプトから解析する方法はありません。


直接あなたの質問ではありませんが、シェルのスクリッティングが特に異なるノードのリモート管理(およびyamlインベントリ)の処理に関するものである場合は、ansibleを確認することをお勧めします
eckes

9
yqシェルでyamlファイルの読み取り/書き込みを使用してみてください。プロジェクトページはこちらです:mikefarah.github.io/yqあなたがしてツールをインストールすることができbrewaptまたはバイナリをダウンロードしてください。値の読み取りは次のように簡単ですyq r some.yaml key.value
vdimitrov 2018

@kenorb JSON!= yml / YAML
swe

私は密接に関連する関数pkuczynskiのgithubを見つけました。その中で最高の(私にとって)は、ジャスペレスのもので、彼自身のgithubで維持されていました
splaisan

回答:


56

私の使用例は、この元の投稿が求めていたものとまったく同じである場合も、そうでない場合もありますが、まったく同じです。

YAMLをbash変数として取得する必要があります。YAMLの深さが1レベルを超えることは決してありません。

YAMLは次のようになります。

KEY:                value
ANOTHER_KEY:        another_value
OH_MY_SO_MANY_KEYS: yet_another_value
LAST_KEY:           last_value

次のような出力:

KEY="value"
ANOTHER_KEY="another_value"
OH_MY_SO_MANY_KEYS="yet_another_value"
LAST_KEY="last_value"

私はこの行で出力を達成しました:

sed -e 's/:[^:\/\/]/="/g;s/$/"/g;s/ *=/=/g' file.yaml > file.sh
  • s/:[^:\/\/]/="/g無視して(URLの場合)、:それを見つけて置き換えます="://
  • s/$/"/g"各行の末尾に追加します
  • s/ *=/=/g 前のすべてのスペースを削除します =

13
何を取得しているのかはわかりませんが、これがすべてのYAMLで機能しない場合は、その通りです。だからこそ、いくつかの資格を持ってオープンしました。当時は他のどの質問よりも答えが良かったので、私のユースケースでうまくいったことを共有しました。これは間違いなく拡張できます。
Curtis Blackwell

3
コードインジェクションにも少しオープンですが、あなたが言ったように一歩前進です
Oriettaxx 2014

1
ローカルで使用するシェルスクリプトを記述したことがあるので、それは私にとって問題ではありませんでした。しかし、あなたがそれを安全にする方法を知っている、そして/または、詳しく述べたいと思うなら、私は間違いなく感謝します。
Curtis Blackwell 14

2
1レベルの深いyamlには多くの形式があります。値は次のインデントされた行に分割できます。シェルが解析しない複数の方法で値を引用することができます。中かっこを使用してすべてを1行で記述できます{KEY: 'value', ...}。そしておそらく他の人。最も重要なのは、結果をシェルコードとして評価するつもりなら、それは非常に安全ではないということです。
Beni Cherniavsky-Paskin 2016年

280

以下は、sedとawkを利用して単純なyamlファイルを解析するbashのみのパーサーです。

function parse_yaml {
   local prefix=$2
   local s='[[:space:]]*' w='[a-zA-Z0-9_]*' fs=$(echo @|tr @ '\034')
   sed -ne "s|^\($s\):|\1|" \
        -e "s|^\($s\)\($w\)$s:$s[\"']\(.*\)[\"']$s\$|\1$fs\2$fs\3|p" \
        -e "s|^\($s\)\($w\)$s:$s\(.*\)$s\$|\1$fs\2$fs\3|p"  $1 |
   awk -F$fs '{
      indent = length($1)/2;
      vname[indent] = $2;
      for (i in vname) {if (i > indent) {delete vname[i]}}
      if (length($3) > 0) {
         vn=""; for (i=0; i<indent; i++) {vn=(vn)(vname[i])("_")}
         printf("%s%s%s=\"%s\"\n", "'$prefix'",vn, $2, $3);
      }
   }'
}

次のようなファイルを理解します。

## global definitions
global:
  debug: yes
  verbose: no
  debugging:
    detailed: no
    header: "debugging started"

## output
output:
   file: "yes"

これを使用して解析すると、次のようになります。

parse_yaml sample.yml

出力されます:

global_debug="yes"
global_verbose="no"
global_debugging_detailed="no"
global_debugging_header="debugging started"
output_file="yes"

また、rubyによって生成されるyamlファイルも認識します。

---
:global:
  :debug: 'yes'
  :verbose: 'no'
  :debugging:
    :detailed: 'no'
    :header: debugging started
  :output: 'yes'

前の例と同じように出力されます。

スクリプト内での一般的な使用法は次のとおりです。

eval $(parse_yaml sample.yml)

parse_yamlは接頭辞引数を受け入れるため、インポートされた設定にはすべて共通の接頭辞が付きます(これにより、名前空間の衝突のリスクが軽減されます)。

parse_yaml sample.yml "CONF_"

収量:

CONF_global_debug="yes"
CONF_global_verbose="no"
CONF_global_debugging_detailed="no"
CONF_global_debugging_header="debugging started"
CONF_output_file="yes"

ファイル内の以前の設定は、後の設定で参照できることに注意してください。

## global definitions
global:
  debug: yes
  verbose: no
  debugging:
    detailed: no
    header: "debugging started"

## output
output:
   debug: $global_debug

もう1つの便利な使い方は、最初にデフォルトファイルを解析し、次にユーザー設定を解析することです。これは、後者の設定が最初の設定を上書きするため機能します。

eval $(parse_yaml defaults.yml)
eval $(parse_yaml project.yml)

3
クールなステファン!yaml -表記をネイティブのbash配列に変換できるとしたらすばらしいでしょう。
quickshiftin 2014

3
これは、awkスクリプトのprintf行を変更すると、非常に簡単になります。ただし、bashは多次元の連想配列をサポートしていないので、配列+値ごとに1つのキーになることに注意してください。うーん、おそらくこれをgithubに移動する必要があります...
Stefan Farestam 14

5
これは、2つのスペースの標準ymlインデントを想定しています。4つのスペースを使用している場合、変数は区切り文字として、たとえばのglobal__debug代わりに2つのアンダースコアを取得しますglobal_debug
k0pernikus

3
こんにちはvaab-多くの読者がシェルから実際のYAMLファイルを解析することを望んでいることは間違いありませんが、結果がどうなるかは(少なくとも私には)はっきりしていません。このスクリプトを使用して、問題を突き止め、標準変数への合理的なマッピングを持つサブセットを定義しました。確かに、実際のYAMLファイルを解析するというより大きな問題に対処したふりはありません。
Stefan Farestam 2014年

3
画面に出力を印刷するだけです。後で値にどのようにアクセスしますか?
16

96

shyamlシェルコマンドラインからYAMLクエリのニーズに合わせてPythonで記述しました。

概要:

$ pip install shyaml      ## installation

例のYAMLファイル(複雑な機能を含む):

$ cat <<EOF > test.yaml
name: "MyName !!"
subvalue:
    how-much: 1.1
    things:
        - first
        - second
        - third
    other-things: [a, b, c]
    maintainer: "Valentin Lab"
    description: |
        Multiline description:
        Line 1
        Line 2
EOF

基本的なクエリ:

$ cat test.yaml | shyaml get-value subvalue.maintainer
Valentin Lab

複雑な値に対するより複雑なループクエリ:

$ cat test.yaml | shyaml values-0 | \
  while read -r -d $'\0' value; do
      echo "RECEIVED: '$value'"
  done
RECEIVED: '1.1'
RECEIVED: '- first
- second
- third'
RECEIVED: '2'
RECEIVED: 'Valentin Lab'
RECEIVED: 'Multiline description:
Line 1
Line 2'

いくつかの重要なポイント:

  • すべてのYAMLタイプと構文の奇妙さは、複数行、引用符付き文字列、インラインシーケンスとして正しく処理されます...
  • \0 パッド付き出力は、複数行入力の操作に使用できます。
  • サブ値を選択するための単純なドット表記(つまりsubvalue.maintainer、有効なキーです)。
  • インデックスによるアクセスはシーケンスに提供されます(つまりsubvalue.things.-1subvalue.thingsシーケンスの最後の要素です)。
  • bashループで使用するために、すべてのシーケンス/構造体要素に一度にアクセスできます。
  • YAMLファイルのサブパート全体を... YAMLとして出力できます。これは、shyamlでさらに操作するために適切にブレンドされます。

その他のサンプルとドキュメントは、shyaml githubページまたはshyaml PyPIページで入手できます


1
これはすごい!出力で空白のyaml値を無視するフラグがあったら、すばらしいでしょう。現在は「null」を出力します。私はそれをenvdirと一緒に使用してdocker-composeファイルをenvdirに出力していますcat docker-compose.yml | shyaml get-value api.environment | grep -v null | awk -F': ' '{print $2 > ("envdir/" $1)}'
JiminyCricket

@JiminyCricket github issue pageを使用してください!私は少なくともこれを追跡してうれしいです。;)
vaab

1
残念ながら、shyaml
途方もなく

42

yqは軽量でポータブルなコマンドラインYAMLプロセッサです

プロジェクトの目的は、jmlまたはyamlファイルのsedです。

https://github.com/mikefarah/yq#readme

例として(ドキュメントから直接盗まれた)、以下のsample.yamlファイルが与えられたとします。

---
bob:
  item1:
    cats: bananas
  item2:
    cats: apples

その後

yq r sample.yaml bob.*.cats

出力されます

- bananas
- apples

フィルタリング機能が不足しているだけ
アントニン

formulae.brew.sh/formula/yqのインストール数は、昨年26,679です。
ダスティンバン

1
@Antoninこれがあなたの言っていることかどうかはわかりませんが、現在いくつかのフィルタリング機能があるようです:mikefarah.gitbook.io/yq/usage/path-expressions
bmaupin

32

Pythonのような一部のインタープリターに小さなスクリプトを渡すことが可能です。RubyとそのYAMLライブラリを使用してこれを行う簡単な方法は次のとおりです。

$ RUBY_SCRIPT="data = YAML::load(STDIN.read); puts data['a']; puts data['b']"
$ echo -e '---\na: 1234\nb: 4321' | ruby -ryaml -e "$RUBY_SCRIPT"
1234
4321

ここで、dataはyamlからの値を含むハッシュ(または配列)です。

おまけとして、それはジキルの前件をうまく解析します。

ruby -ryaml -e "puts YAML::load(open(ARGV.first).read)['tags']" example.md

1
使えますか?yamlをルビーインタープリターにエコーで配置しました。しかし、残りのbashスクリプトでこの変数をどのように使用すべきですか?
Znikの2014

はい、使用できます。RUBY_SCRIPT変数は(と実行の代わりにファイルに書き込むことができるRubyスクリプトですruby -ryaml <rubyscript_filename>)。これには、入力テキストを出力テキストに変換するロジックが含まれており、コンテンツをdata変数に内部的に格納します。エコーはyamlテキストを出力しますが、cat <yaml_filename>代わりにファイルのコンテンツをパイプするために使用できます。
ラファエル

申し訳ありませんが、上の例ではこれが表示されません。最初に、変数RUBY_SCRIPTは、ルビインタープリタのコードを保持します。次のecho -eは、yamlデータをシミュレートします。これは、rubyインタープリターにパイルリダイレクトされます。これは、インラインスクリプトとしてルビコードを呼び出し、最後に出力して 'a'および 'b'変数の例を出力します。次に、残りの実行可能コードの変数をbashにロードする場所はどこですか?回避策は1つしかありません。ruby outoutをtemporary_fileに入れます。これは次の行を含む必要があります:variable = 'value'、その後 'でbashにロードします。temporary_file '。しかし、これは解決策ではなく回避策です。
Znik 2014年

1
@Znikがstdoutで何かを得て、stdinでstdout供給された何かによって生成された場合、残りはbashコーダーの手に依存します(また、変数に入力する必要がある場合は、依存する必要はありません一時ファイル!x=$(...)またはを使用しますread a b c < <(...))。したがって、これは、YAMLファイルで何をフェッチするかを正確に理解し、このデータにアクセスするためのルビ行の記述方法を知っている場合に有効なソリューションです。それが荒くても、それは私見の概念の概念の完全な証明です。それでも、完全なbash抽象化が提供されないのは事実です。
vaab 2014年

はい、そうです。あなたは厳格です。そのトリックをありがとう。1つの変数を使用するのは簡単です。しかし、多くのwariableはそうではありません。変数リストを読み取るトリック<<(stdoutへの実行)は非常に
便利です

23

現在、Python3とPyYAMLは依存関係が非常に簡単であることを考えると、次のことが役立つ場合があります。

yaml() {
    python3 -c "import yaml;print(yaml.safe_load(open('$1'))$2)"
}

VALUE=$(yaml ~/my_yaml_file.yaml "['a_key']")

私はshyamlが大好きですが、切断されたシステムではこれは命の恩人です。大部分のpython2、たとえばRHELでも動作するはずです。
rsaw

2
yaml.safe_load安全かもしれないので多分使います。pyyaml.org/wiki/PyYAMLDocumentation
ジョーダンスチュワート

12

ここにステファン・ファレスタムの答えの拡張版があります:

function parse_yaml {
   local prefix=$2
   local s='[[:space:]]*' w='[a-zA-Z0-9_]*' fs=$(echo @|tr @ '\034')
   sed -ne "s|,$s\]$s\$|]|" \
        -e ":1;s|^\($s\)\($w\)$s:$s\[$s\(.*\)$s,$s\(.*\)$s\]|\1\2: [\3]\n\1  - \4|;t1" \
        -e "s|^\($s\)\($w\)$s:$s\[$s\(.*\)$s\]|\1\2:\n\1  - \3|;p" $1 | \
   sed -ne "s|,$s}$s\$|}|" \
        -e ":1;s|^\($s\)-$s{$s\(.*\)$s,$s\($w\)$s:$s\(.*\)$s}|\1- {\2}\n\1  \3: \4|;t1" \
        -e    "s|^\($s\)-$s{$s\(.*\)$s}|\1-\n\1  \2|;p" | \
   sed -ne "s|^\($s\):|\1|" \
        -e "s|^\($s\)-$s[\"']\(.*\)[\"']$s\$|\1$fs$fs\2|p" \
        -e "s|^\($s\)-$s\(.*\)$s\$|\1$fs$fs\2|p" \
        -e "s|^\($s\)\($w\)$s:$s[\"']\(.*\)[\"']$s\$|\1$fs\2$fs\3|p" \
        -e "s|^\($s\)\($w\)$s:$s\(.*\)$s\$|\1$fs\2$fs\3|p" | \
   awk -F$fs '{
      indent = length($1)/2;
      vname[indent] = $2;
      for (i in vname) {if (i > indent) {delete vname[i]; idx[i]=0}}
      if(length($2)== 0){  vname[indent]= ++idx[indent] };
      if (length($3) > 0) {
         vn=""; for (i=0; i<indent; i++) { vn=(vn)(vname[i])("_")}
         printf("%s%s%s=\"%s\"\n", "'$prefix'",vn, vname[indent], $3);
      }
   }'
}

このバージョンは、-辞書とリストの表記法と短い表記法をサポートしています。次の入力:

global:
  input:
    - "main.c"
    - "main.h"
  flags: [ "-O3", "-fpic" ]
  sample_input:
    -  { property1: value, property2: "value2" }
    -  { property1: "value3", property2: 'value 4' }

この出力を生成します:

global_input_1="main.c"
global_input_2="main.h"
global_flags_1="-O3"
global_flags_2="-fpic"
global_sample_input_1_property1="value"
global_sample_input_1_property2="value2"
global_sample_input_2_property1="value3"
global_sample_input_2_property2="value 4"

ご覧の-とおり、アイテムごとに異なる変数名を取得するために、アイテムには自動的に番号が付けられます。にbash何の多次元配列が存在しないので、これは回避する1つの方法は、あります。複数のレベルがサポートされています。@briceburgによって言及される末尾の空白の問題を回避するには、値を一重引用符または二重引用符で囲む必要があります。ただし、それでもいくつかの制限があります。辞書やリストを拡張すると、値にカンマが含まれている場合に誤った結果が生成される可能性があります。また、複数の行にまたがる値(ssh-keysなど)のようなより複雑な構造は(まだ)サポートされていません。

コードに関する簡単な説明:最初のsedコマンドは、辞書の短い形式{ key: value, ...}を標準に拡張し、より単純なyamlスタイルに変換します。2番目のsed呼び出しは、リストの短い表記について同じことを行い、表記の[ entry, ... ]ある項目別リストに変換します-。3番目のsed呼び出しは、通常の辞書を処理した元の呼び出しですが、-インデント付きのリストを処理するように追加されています。このawkパートでは、インデントレベルごとにインデックスを導入し、変数名が空の場合(リストを処理する場合)にインデックスを増やします。空のvnameの代わりに、カウンターの現在の値が使用されます。1レベル上がると、カウンターはゼロになります。

編集:私はこれのためにgithubリポジトリを作成しました。


11

パーサーがYAMLドキュメントから何を抽出するかに依存するため、言うのは難しいです。シンプルなケースでは、あなたが使用することができるかもしれませんgrepcutawkあなたが使用する必要があり、より複雑な構文解析についてなどを本格Pythonのようなライブラリを解析PyYAMLとか、YAML :: Perlの


11

Yayというパーサーを書いたところです。YamlはYamlesqueではありません!)YAMLの小さなサブセットであるYamlesqueを解析します。したがって、Bash用の100%準拠のYAMLパーサーを探している場合、これはそうではありません。ただし、OPを引用すると、YAMLに似た、技術者以外のユーザーが編集するのが可能な限り簡単な構造化構成ファイルが必要な場合は、これが興味深いかもしれません。

これは以前の答えに基づいていますが、基本的な変数の代わりに連想配列を書き込みます(はい、Bash 4.xが必要です)。これは、事前にキーを知らなくてもデータを解析できるようにするため、データ駆動型のコードを記述できます。

キー/値配列要素と同様に、各配列には、keysキー名のリストをchildren含む配列、子配列の名前を含む配列、およびparentその親を参照するキーがあります。

このはYamlesqueの例です。

root_key1: this is value one
root_key2: "this is value two"

drink:
  state: liquid
  coffee:
    best_served: hot
    colour: brown
  orange_juice:
    best_served: cold
    colour: orange

food:
  state: solid
  apple_pie:
    best_served: warm

root_key_3: this is value three

ここではそれを使用する方法を示す例です。

#!/bin/bash
# An example showing how to use Yay

. /usr/lib/yay

# helper to get array value at key
value() { eval echo \${$1[$2]}; }

# print a data collection
print_collection() {
  for k in $(value $1 keys)
  do
    echo "$2$k = $(value $1 $k)"
  done

  for c in $(value $1 children)
  do
    echo -e "$2$c\n$2{"
    print_collection $c "  $2"
    echo "$2}"
  done
}

yay example
print_collection example

出力:

root_key1 = this is value one
root_key2 = this is value two
root_key_3 = this is value three
example_drink
{
  state = liquid
  example_coffee
  {
    best_served = hot
    colour = brown
  }
  example_orange_juice
  {
    best_served = cold
    colour = orange
  }
}
example_food
{
  state = solid
  example_apple_pie
  {
    best_served = warm
  }
}

そしてここにパーサーがあります:

yay_parse() {

   # find input file
   for f in "$1" "$1.yay" "$1.yml"
   do
     [[ -f "$f" ]] && input="$f" && break
   done
   [[ -z "$input" ]] && exit 1

   # use given dataset prefix or imply from file name
   [[ -n "$2" ]] && local prefix="$2" || {
     local prefix=$(basename "$input"); prefix=${prefix%.*}
   }

   echo "declare -g -A $prefix;"

   local s='[[:space:]]*' w='[a-zA-Z0-9_]*' fs=$(echo @|tr @ '\034')
   sed -n -e "s|^\($s\)\($w\)$s:$s\"\(.*\)\"$s\$|\1$fs\2$fs\3|p" \
          -e "s|^\($s\)\($w\)$s:$s\(.*\)$s\$|\1$fs\2$fs\3|p" "$input" |
   awk -F$fs '{
      indent       = length($1)/2;
      key          = $2;
      value        = $3;

      # No prefix or parent for the top level (indent zero)
      root_prefix  = "'$prefix'_";
      if (indent ==0 ) {
        prefix = "";          parent_key = "'$prefix'";
      } else {
        prefix = root_prefix; parent_key = keys[indent-1];
      }

      keys[indent] = key;

      # remove keys left behind if prior row was indented more than this row
      for (i in keys) {if (i > indent) {delete keys[i]}}

      if (length(value) > 0) {
         # value
         printf("%s%s[%s]=\"%s\";\n", prefix, parent_key , key, value);
         printf("%s%s[keys]+=\" %s\";\n", prefix, parent_key , key);
      } else {
         # collection
         printf("%s%s[children]+=\" %s%s\";\n", prefix, parent_key , root_prefix, key);
         printf("declare -g -A %s%s;\n", root_prefix, key);
         printf("%s%s[parent]=\"%s%s\";\n", root_prefix, key, prefix, parent_key);
      }
   }'
}

# helper to load yay data file
yay() { eval $(yay_parse "$@"); }

リンクされたソースファイルにはいくつかのドキュメントがあり、以下はコードの機能の簡単な説明です。

yay_parse関数は、最初に見つけinputファイルをか、終了ステータス1で終了します。次に、prefix明示的に指定された、またはファイル名から派生したデータセットを決定します。

有効なbashコマンドを標準出力に書き込み、実行すると、入力データファイルの内容を表す配列を定義します。これらの最初のものはトップレベルの配列を定義します:

echo "declare -g -A $prefix;"

配列宣言は連想(-A)であることに注意してください。これはBashバージョン4の機能です。宣言もグローバル(-g)なので、関数内で実行できますが、yayヘルパーのようなグローバルスコープで使用できます。

yay() { eval $(yay_parse "$@"); }

入力データは最初にで処理されsedます。有効なYamlesqueフィールドをASCII ファイル区切り文字で区切り、値フィールドを囲む二重引用符を削除する前に、Yamlesqueフォーマット仕様に一致しない行をドロップします。

 local s='[[:space:]]*' w='[a-zA-Z0-9_]*' fs=$(echo @|tr @ '\034')
 sed -n -e "s|^\($s\)\($w\)$s:$s\"\(.*\)\"$s\$|\1$fs\2$fs\3|p" \
        -e "s|^\($s\)\($w\)$s:$s\(.*\)$s\$|\1$fs\2$fs\3|p" "$input" |

2つの式は似ています。2つ目は引用符で囲まれていない値を選択するのに対し、最初のものは引用符で囲まれた値を選択するためです。

ファイル区切り、非印刷可能文字として、入力されたデータであることにくい、ので(28 /ヘキサン12 /進034)が使用されます。

結果はパイプawk処理され、入力が一度に1行ずつ処理されます。FS文字を使用して、各フィールドを変数に割り当てます。

indent       = length($1)/2;
key          = $2;
value        = $3;

すべての行にはインデント(ゼロの場合もある)とキーがありますが、すべての行に値があるわけではありません。先頭の空白を含む最初のフィールドの長さを2で割る行のインデントレベルを計算します。インデントのない最上位のアイテムはインデントレベル0です。

次に、prefix現在のアイテムに何を使用するかを決定します。これがキー名に追加されて配列名になります。ありますroot_prefix、データセット名とアンダースコアとして定義されているトップレベルのアレイの:

root_prefix  = "'$prefix'_";
if (indent ==0 ) {
  prefix = "";          parent_key = "'$prefix'";
} else {
  prefix = root_prefix; parent_key = keys[indent-1];
}

parent_key現在の行のインデントレベル以上のインデントレベルでの鍵であり、現在の行がその一部となっているコレクションを表します。コレクションのキーと値のペアは、prefixとの連結として定義された名前で配列に格納されparent_keyます。

トップレベル(インデントレベル0)の場合、データセットプレフィックスは親キーとして使用されるため、プレフィックスはありません(次のように設定されています) "")。他のすべての配列には、ルートプレフィックスがプレフィックスされます。

次に、現在のキーが、キーを含む(awk-internal)配列に挿入されます。この配列はawkセッション全体を通じて持続するため、前の行によって挿入されたキーが含まれています。キーは、そのインデントを配列インデックスとして使用して配列に挿入されます。

keys[indent] = key;

この配列には前の行のキーが含まれているため、現在の行のインデントレベルよりもインデントレベルが大きいキーは削除されます。

 for (i in keys) {if (i > indent) {delete keys[i]}}

これにより、キーチェーンを含むキー配列がインデントレベル0のルートから現在の行まで残ります。これは、前の行が現在の行よりも深くインデントされたときに残る古いキーを削除します。

最後のセクションはbashコマンドを出力します。値のない入力行は新しいインデントレベル(YAML用語ではコレクション)を開始し、値のある入力行は現在のコレクションにキーを追加します。

コレクションの名前は、現在の行のprefixとを連結したものparent_keyです。

キーに値がある場合、その値を持つキーは次のように現在のコレクションに割り当てられます。

printf("%s%s[%s]=\"%s\";\n", prefix, parent_key , key, value);
printf("%s%s[keys]+=\" %s\";\n", prefix, parent_key , key);

最初のステートメントは、キーにちなんで名付けられた連想配列要素に値を割り当てるコマンドを出力し、2番目のステートメントは、コレクションのスペース区切りkeysリストにキーを追加するコマンドを出力します。

<current_collection>[<key>]="<value>";
<current_collection>[keys]+=" <key>";

キーに値がない場合、次のように新しいコレクションが開始されます。

printf("%s%s[children]+=\" %s%s\";\n", prefix, parent_key , root_prefix, key);
printf("declare -g -A %s%s;\n", root_prefix, key);

最初のステートメントは、現在のコレクションのスペース区切りのchildrenリストに新しいコレクションを追加するコマンドを出力し、2番目のステートメントは、新しいコレクションの新しい連想配列を宣言するコマンドを出力します。

<current_collection>[children]+=" <new_collection>"
declare -g -A <new_collection>;

からのすべての出力は、yay_parsebashコマンドevalまたはsource組み込みコマンドによってbashコマンドとして解析できます。


これをGitHubのプロジェクトにすることを検討しましたか?それともすでにですか?
ダニエル2016

@daniel、それはGitHubにありますが、独自のリポジトリにはありません- ここで見つけることができます。examplesおよびusr/libディレクトリを参照してください。これらは質問への私の回答にリンクされています。興味があれば、それを独自のリポジトリに分割することができます。
starfry 2016

4
YAYの称賛。最初は純粋なbashに書き直しましたが、自分自身を止めることができず、配列とネストされた構造をサポートし、お互いの名前を踏まない基本的なパーサーとして再実装しました。それはATのgithub.com/binaryphile/y2s
バイナリフィール2017

5
perl -ne 'chomp; printf qq/%s="%s"\n/, split(/\s*:\s*/,$_,2)' file.yml > file.sh

フラット構成でのみ役立ちます。構造化yamlには適用されません。別の、一時的なfile.shの使用を防ぐ方法は?
Znik、2014

5

もう1つのオプションは、YAMLをJSONに変換し、jqを使用してJSON表現とやり取りして、情報を抽出するか編集することです。

私はこの接着剤を含む簡単なbashスクリプトを書きました-GitHubのY2Jプロジェクトを参照してください


2

あなたが単一の値が必要な場合は、JSONにしてフィードに自分のYAMLドキュメントを変換するツールでしjqたとえば、yq

sample.yamlの内容:

---
bob:
  item1:
    cats: bananas
  item2:
    cats: apples
  thing:
    cats: oranges

例:

$ yq -r '.bob["thing"]["cats"]' sample.yaml 
oranges

1

私はこれが非常に具体的であることを知っていますが、私の答えは特定のユーザーに役立つと思います。
あなたが持っている場合nodenpm、あなたのマシンにインストールされ、あなたが使用することができますjs-yaml
最初のインストール:

npm i -g js-yaml
# or locally
npm i js-yaml

その後、あなたのbashスクリプトで

#!/bin/bash
js-yaml your-yaml-file.yml

また、使用しているjq場合は、そのようなことを行うことができます

#!/bin/bash
json="$(js-yaml your-yaml-file.yml)"
aproperty="$(jq '.apropery' <<< "$json")"
echo "$aproperty"

なぜならjs-yaml、yamlファイルをjson文字列リテラルに変換するからです。次に、UNIXシステムの任意のjsonパーサーで文字列を使用できます。


1

Python 2とPyYAMLを使用している場合は、私が作成したparse_yaml.pyというパーサーを使用できます。これにより、プレフィックスを選択して(同様の変数を持つ複数のファイルがある場合)、yamlファイルから単一の値を選択できるようになります。

たとえば、次のyamlファイルがある場合:

staging.yaml:

db:
    type: sqllite
    host: 127.0.0.1
    user: dev
    password: password123

prod.yaml:

db:
    type: postgres
    host: 10.0.50.100
    user: postgres
    password: password123

競合することなく両方をロードできます。

$ eval $(python parse_yaml.py prod.yaml --prefix prod --cap)
$ eval $(python parse_yaml.py staging.yaml --prefix stg --cap)
$ echo $PROD_DB_HOST
10.0.50.100
$ echo $STG_DB_HOST
127.0.0.1

そして、チェリーでさえあなたが望む価値を選びます。

$ prod_user=$(python parse_yaml.py prod.yaml --get db_user)
$ prod_port=$(python parse_yaml.py prod.yaml --get db_port --default 5432)
$ echo prod_user
postgres
$ echo prod_port
5432

1

golangで記述されたyqと同等のものを使用できます。

./go-yg -yamlFile /home/user/dev/ansible-firefox/defaults/main.yml -key
firefox_version

戻り値:

62.0.3

0

Grunt(JavaScriptタスクランナー)の使用を検討することもできます。シェルと簡単に統合できます。YAML(grunt.file.readYAML)とJSON(grunt.file.readJSON)ファイルのます。

これはGruntfile.js(またはGruntfile.coffee)でタスクを作成することで実現できます。例:

module.exports = function (grunt) {

    grunt.registerTask('foo', ['load_yml']);

    grunt.registerTask('load_yml', function () {
        var data = grunt.file.readYAML('foo.yml');
        Object.keys(data).forEach(function (g) {
          // ... switch (g) { case 'my_key':
        });
    });

};

次に、シェルから単に実行しますgrunt foogrunt --help使用可能なタスクを確認します)。

さらに、より多くのあなたが実装することができますexec:foo(タスクをgrunt-execタスクから渡された入力変数(と)foo: { cmd: 'echo bar <%= foo %>' }別のコマンドにパイプして、その後、あなたが好きなフォーマットで出力を印刷するために)。


うなり声に似たようなツールは、それを呼び出して、もあり一気追加のプラグインと一口-YAML

インストール方法: npm install --save-dev gulp-yaml

使用例:

var yaml = require('gulp-yaml');

gulp.src('./src/*.yml')
  .pipe(yaml())
  .pipe(gulp.dest('./dist/'))

gulp.src('./src/*.yml')
  .pipe(yaml({ space: 2 }))
  .pipe(gulp.dest('./dist/'))

gulp.src('./src/*.yml')
  .pipe(yaml({ safe: true }))
  .pipe(gulp.dest('./dist/'))

YAML形式を処理するためのその他のオプションについては、YAMLサイトで、その形式の解析に役立つ利用可能なプロジェクト、ライブラリ、その他のリソースを確認してください。


その他のツール:

  • Jshon

    JSONを解析、読み取り、作成します


0

私の答えは具体的ですが、PHPSymfonyがすでにインストールされている場合は、SymfonyのYAMLパーサーを使用すると非常に便利です。

例えば:

php -r "require '$SYMFONY_ROOT_PATH/vendor/autoload.php'; \
    var_dump(\Symfony\Component\Yaml\Yaml::parse(file_get_contents('$YAML_FILE_PATH')));"

ここではvar_dump、解析された配列を出力するために単に使用しましたが、もちろん、さらに多くのことができます... :)

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