Bir Linux kabuk betiğinden bir YAML dosyasını nasıl ayrıştırabilirim?


193

Teknik olmayan bir kullanıcının düzenlemek için mümkün olduğunca kolay bir yapılandırılmış yapılandırma dosyası sağlamak istiyorum (ne yazık ki bir dosya olması gerekir) ve bu yüzden YAML kullanmak istedim. Ancak bunu bir Unix kabuk komut dosyasından ayrıştırma yolu bulamıyorum.


doğrudan sorunuz değil, ancak kabuk yazmanız özellikle farklı düğümlerin (ve bir yaml envanterinin) uzaktan yönetimi ile ilgili ise ansible'a bakmak isteyebilirsiniz
eckes

9
yqYaml dosyalarını kabukta okumak / yazmak için kullanmayı deneyin . Proje sayfası burada: mikefarah.github.io/yq Sen ile aracı yükleyebilir brew, aptveya ikili indirin. Bir değeri okumak kadar basityq r some.yaml key.value
vdimitrov

@kenorb JSON! = yml / YAML
swe

En iyi (benim için) jasperes'in kendi
github'ında

Yanıtlar:


56

Kullanım durumum, bu orijinal yazının sorduğu ile aynı olabilir veya olmayabilir, ancak kesinlikle benzer.

Bazı YAML'yi bash değişkenleri olarak çekmem gerekiyor. YAML asla bir seviye derinden fazla olmayacaktır.

YAML şöyle görünür:

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

Çıktı benzeri bir dis:

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

Bu hat ile çıktıya ulaştım:

sed -e 's/:[^:\/\/]/="/g;s/$/"/g;s/ *=/=/g' file.yaml > file.sh
  • s/:[^:\/\/]/="/ggörmezden gelirken bulur :ve değiştirir (URL'ler için)="://
  • s/$/"/g"her satırın sonuna eklenir
  • s/ *=/=/g önceki tüm alanları kaldırır =

13
Neye ulaştığınızdan emin değilsiniz, ancak bu tüm YAML için işe yaramazsa, haklısınız. Bu yüzden birkaç nitelikle açtım. Kullanım durumumda işe yarayanı paylaştım, çünkü o sırada soruyu diğerlerinden daha iyi yanıtladı. Bu kesinlikle genişletilebilir.
Curtis Blackwell

3
kod enjeksiyonuna da biraz açık, ama dediğin gibi bir adım ileri
Oriettaxx

1
Ben sadece yerel olarak kullanmak için kabuk komut dosyaları yazdım, bu yüzden bu benim için bir endişe değildi. Ancak, nasıl güvende tutacağınızı ve / veya ayrıntılı bir şekilde öğrenmek istiyorsanız, kesinlikle minnettar olurum.
Curtis Blackwell

2
Tek düzey derin yaml'ın birçok formu vardır - değerler aşağıdaki girintili çizgiye ayrılabilir; değerler kabuğun ayrıştırılmayacağı birden çok yolla alıntılanabilir; her şey bir satırda kaşlı ayraçla yazılabilir {KEY: 'value', ...}:; ve muhtemelen diğerleri. En önemlisi, sonucu kabuk kodu olarak değerlendirmeyi düşünüyorsanız, bu çok güvensizdir.
Beni Cherniavsky-Paskin

281

Basit yaml dosyalarını ayrıştırmak için sed ve awk kullanan tek bash ayrıştırıcısı:

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);
      }
   }'
}

Aşağıdaki gibi dosyaları anlar:

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

## output
output:
   file: "yes"

Hangi, kullanılarak ayrıştırıldığında:

parse_yaml sample.yml

çıktı olacak:

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

ayrıca yakut sembollerini içerebilen yakut tarafından oluşturulan yaml dosyalarını da anlar, örneğin:

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

ve önceki örnekteki ile aynı çıktıyı alır.

bir komut dosyasında tipik kullanım:

eval $(parse_yaml sample.yml)

parse_yaml, içe aktarılan ayarların ortak bir önek (ad alanı çarpışma riskini azaltacağı) olması için bir önek bağımsız değişkenini kabul eder.

parse_yaml sample.yml "CONF_"

verim:

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

Bir dosyadaki önceki ayarlara daha sonraki ayarlarla atıf yapılabileceğini unutmayın:

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

## output
output:
   debug: $global_debug

Başka bir güzel kullanım önce bir varsayılanlar dosyasını ve daha sonra ikinci ayarların ilkini geçersiz kıldığı için çalışan kullanıcı ayarlarını ayrıştırmaktır:

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

3
Harika Stefan! Yaml -gösterimini yerel bash dizilerine de çevirebilirse şaşırtıcı olurdu !
quickshiftin

3
Awk betiğindeki printf satırını değiştirirseniz bunu yapmak oldukça kolay olacaktır. Ancak bash'ın çok boyutlu ilişkilendirilebilir diziler için desteği olmadığını unutmayın, böylece bir dizi + değer başına tek bir anahtar ile sonuçlanırsınız. Hmm, muhtemelen bunu
github'a taşımalıyız

5
Bu, 2 alanın standart yml girintisini bekler. 4 boşluk kullanıyorsanız, değişkenler sınırlayıcı olarak, örneğin global__debugyerine iki alt çizgi alır global_debug.
k0pernikus

3
Merhaba vaab - Birçok okuyucunun gerçek YAML dosyalarını kabuktan ayrıştırmak istediğinden emin olmanıza rağmen, sonucun ne olacağı oldukça açık değil (en azından benim için). Bu komut dosyasıyla, sorunu bir bıçakla aldım ve standart değişkenlere makul bir eşleme olan bir alt kümeyi tanımladım. Gerçek YAML dosyalarını ayrıştırmanın daha büyük sorununa değinmiş gibi bir kesinlikle yoktur.
Stefan Farestam

3
Yalnızca çıktıyı ekrana yazdırır. Daha sonra değerlere nasıl erişirsiniz?
sattu

96

shyamlKabuk komut satırından YAML sorgu ihtiyaçları için python yazdım .

Genel bakış:

$ pip install shyaml      ## installation

Örnek YAML dosyası (karmaşık özelliklere sahip):

$ 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

Temel sorgu:

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

Karmaşık değerler üzerinde daha karmaşık döngü sorgusu:

$ 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'

Birkaç önemli nokta:

  • tüm YAML türleri ve sözdizimi tuhaflıkları, çok satırlı, tırnaklı dizeler, satır içi diziler olarak doğru şekilde ele alınır ...
  • \0 dolgulu çıktı katı çok satırlı giriş manipülasyonu için kullanılabilir.
  • alt değerleri seçmek için basit noktalı gösterim (yani: subvalue.maintainergeçerli bir anahtardır).
  • dizinlere erişim dizilere sağlanır (yani: dizinin subvalue.things.-1son elemanıdır subvalue.things).
  • bash döngülerinde kullanılmak üzere tek seferde tüm dizi / yapı elemanlarına erişim.
  • bir YAML dosyasının tüm alt bölümünü çıktı olarak ... YAML olarak gönderebilirsiniz, bu da shyaml ile daha fazla manipülasyon için iyi uyum sağlar.

Daha fazla örnek ve dokümantasyon shyaml github sayfasında veya shyaml PyPI sayfasında bulunabilir .


1
Bu harika! Çıktıda boş olan yaml değerlerini yoksaymak için bir bayrak olsaydı harika olurdu. Şu anda "null" çıktı veriyor. Envdir'e docker-compose dosyası çıkarmak için envdir ile birlikte kullanıyorumcat docker-compose.yml | shyaml get-value api.environment | grep -v null | awk -F': ' '{print $2 > ("envdir/" $1)}'
JiminyCricket

@JiminyCricket Lütfen github sayı sayfasını kullanın! En azından bunu takip etmekten memnuniyet duyarım. ;)
vaab

1
Ne yazık ki, shyamlgülünç yavaş
nowox

43

yq hafif ve taşınabilir bir komut satırı YAML işlemcisidir

Projenin amacı yaml dosyalarının jq veya sed'i olmaktır .

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

Bir örnek olarak (doğrudan belgelerden çalınmıştır ), aşağıdaki örnek bir dosyaya bakıldığında:

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

sonra

yq r sample.yaml bob.*.cats

çıktı olacak

- bananas
- apples

sadece filtreleme özelliklerinden yoksun
Antonin

formulae.brew.sh/formula/yq 26679 geçen yıl boyunca yüklemek vardır.
dustinevan

1
@Antonin Bu ne demek istediğinden emin değilim ama şimdi bazı filtreleme yetenekleri var gibi görünüyor: mikefarah.gitbook.io/yq/usage/path-expressions
bmaupin

32

Python gibi bazı tercümanlara küçük bir senaryo geçirmek mümkündür. Ruby ve YAML kütüphanesini kullanarak bunu yapmanın kolay bir yolu şudur:

$ 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

, burada datayaml değerlerine sahip bir karma (veya dizi) bulunur.

Bonus olarak, Jekyll'in ön meselesini gayet iyi çözümleyecek.

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

1
kullanılabilir mi? yakut çevirmene yankı ile yaml koydunuz. ancak bu değişkeni bash betiğinin geri kalanında nasıl kullanmalıyım?
Znik

Evet, kullanılabilir. RUBY_SCRIPTDeğişken bir dosya yerine (run ile yazılabilir bir yakut script ruby -ryaml <rubyscript_filename>). İçeriği datadeğişkene dahili olarak depolayarak, giriş metnini bazı çıkış metnine dönüştürmek için mantık içerir . Yankı bir yaml metni çıkarır, ancak cat <yaml_filename>bunun yerine bir dosyanın içeriğini yönlendirmek için kullanabilirsiniz .
Rafael

Üzgünüm ama bunu yukarıdaki örnekte göremiyorum. İlk değişkente RUBY_SCRIPT yakut yorumlayıcı kodunu tutar. Sonraki eko-e herhangi bir yaml verisini simüle eder, bu yığın yakut yorumlayıcıya yönlendirilir. Bu, yakut kodunu satır içi komut dosyası olarak adlandırır ve son olarak 'a' ve 'b' değişkenlerinin çıktılarını yazdırır. Sonra onun dinlenme yürütülebilir kodu için bash değişken yükleme nerede? Yalnızca bir geçici çözüm görüyorum. Ruby_Outu geçici_dosya içine koyarak, satırları conain gerekir: değişken = 'değer' ve bundan sonra bash içine 'ile yükleyin. geçici dosya'. ancak bu çözüm değil, geçici bir çözümdür.
Znik

1
@Znik, stdin ile beslenen bir şey tarafından üretilen, stdin ile beslenen bir şeyle üretildiğinde, geri kalanı bas kodlayıcısının ellerine dayanır (ve bir hatırlatma olarak, stdoutdeğişkene beslenmeniz gerekiyorsa, güvenmenize gerek yoktur) geçici dosyalar! kullanın x=$(...)ve hatta read a b c < <(...)). Bu nedenle, YAML dosyasında tam olarak ne almak istediğinizi ve bu verilere erişmek için yakut satırları nasıl yazacağınızı bildiğinizde geçerli bir çözümdür. Sert olsa bile, IMHO fikrinin tam bir kanıtıdır. Yine de size tam bir bash soyutlaması sağlamadığı doğrudur.
vaab

Evet öyle. Sen güçlüsün. Bu numara için teşekkür ederim. Bir değişken kullanmak basittir. ama pek çok wariable değildir. değişken listesi okuma ile hile <<(stdout yürütme) çok yararlı :)
Znik

23

Python3 ve PyYAML'nin günümüzde karşılamak için oldukça kolay bağımlılıklar olduğu düşünüldüğünde, aşağıdakiler yardımcı olabilir:

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

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

Shyaml'i seviyorum, ancak bağlantısı kesilmiş sistemlerde bu bir hayat kurtarıcı. Python2'nun büyük çoğunluğu ile de çalışmalıdır, örn. RHEL.
1919'da testere

2
Belki yaml.safe_loaddaha güvenli olduğu için kullanın . pyyaml.org/wiki/PyYAMLDocumentation
Jordan Stewart

14

burada Stefan Farestam'ın cevabının genişletilmiş bir versiyonu:

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);
      }
   }'
}

Bu sürüm, -sözlükler ve listeler için gösterimi ve kısa gösterimi destekler . Aşağıdaki giriş:

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

bu çıktıyı üretir:

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"

-her öğe için farklı değişken adları elde etmek amacıyla öğelerin otomatik olarak numaralandırıldığını görebilirsiniz . İçinde bashçok boyutlu diziler yoktur, bu yüzden bu geçici bir çözümdür. Birden çok seviye desteklenir. @Briceburg tarafından belirtilen sondaki beyaz boşluklarla ilgili sorunu çözmek için, değerleri tek veya çift tırnak içine almalısınız. Ancak, yine de bazı sınırlamalar vardır: Sözlükler ve listelerin genişletilmesi, değerler virgül içerdiğinde yanlış sonuçlar verebilir. Ayrıca, birden çok satıra yayılan değerler (ssh tuşları gibi) gibi daha karmaşık yapılar (henüz) desteklenmemektedir.

Kod hakkında birkaç kelime: İlk sedkomut sözlüklerin kısa biçimini { key: value, ...}düzenli olarak genişletir ve daha basit yaml stiline dönüştürür. İkinci sedçağrı, listelerin kısa gösterimi için aynısını yapar ve gösterimle [ entry, ... ]birlikte ayrıntılı bir listeye dönüştürür -. Üçüncü sedçağrı, normal sözlükleri işleyen orijinaldir, şimdi listeleri -ve girintileri içeren işlemlerle birlikte eklenir. awkDeğişken adı boşken kısmı (yani bir liste işlerken) her girinti seviyesi ve artar bunu için bir dizin tanıtır. Sayaçların geçerli değeri boş vname yerine kullanılır. Bir seviye yukarı çıkarken sayaçlar sıfırlanır.

Düzenleme: Bunun için bir github deposu oluşturdum .


11

Söylemesi zor çünkü ayrıştırıcının YAML belgenizden ne çıkarmasını istediğinize bağlı. Basit durumlarda, kullanmak mümkün olabilir grep, cut, awkkullanmak gerekir daha karmaşık ayrıştırma vb tam gelişmiş Python'un olarak kütüphane böyle ayrıştırma PyYAML veya YAML :: Perl .


11

Az önce Yay dediğim bir ayrıştırıcı yazdım ! ( Yaml Yamlesque değil! ) YAML'ın küçük bir alt kümesi olan Yamlesque'i ayrıştırır . Yani, Bash için% 100 uyumlu bir YAML ayrıştırıcı arıyorsanız, o zaman bu değil. Bununla birlikte, OP'den alıntı yapmak için, teknik olmayan bir kullanıcının YAML benzeri düzenlemesini mümkün olduğunca kolay bir şekilde yapılandırılmış bir yapılandırma dosyası istiyorsanız , bu ilgi çekici olabilir.

Bu oluyor önceki yanıtıyla inspred ama ilişkilendirilebilir diziler (yazar evet, Bash 4.x gerektirir ) yerine temel değişkenleri. Bunu, verilerin anahtarlar hakkında önceden bilgi sahibi olmadan ayrıştırılmasına izin verecek şekilde yapar, böylece veriye dayalı kod yazılabilir.

Anahtar / değer dizisi öğelerinin yanı sıra, her dizide bir keysanahtar ad listesi children, alt dizilerin adlarını içeren bir dizi parentve üst öğesine başvuruda bulunan bir anahtar bulunur.

Bu Yamlesque'ye bir örnektir:

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

İşte nasıl kullanılacağını gösteren bir örnek:

#!/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

hangi çıktılar:

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
  }
}

Ve işte ayrıştırıcı:

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 "$@"); }

Bağlantılı kaynak dosyasında bazı belgeler vardır ve aşağıda kodun ne yaptığına dair kısa bir açıklama bulunmaktadır.

yay_parseİlkönce fonksiyon, bulur input: 1. Bir sonraki bir çıkış durumu ile bir dosya ya da çıkar, bu veri setini belirler prefix, ayrı bir şekilde, belirtilen ya da dosya adı türetilen.

bashYürütülürse, giriş veri dosyasının içeriğini temsil eden dizileri tanımlayan standart çıktısına geçerli komutlar yazar . Bunlardan ilki üst düzey diziyi tanımlar:

echo "declare -g -A $prefix;"

Dizi bildirimlerinin -ABash sürüm 4'ün bir özelliği olan ilişkisel ( ) olduğunu unutmayın . Bildirimler de global ( -g) işlevidir, bu nedenle bir işlevde yürütülebilirler, ancak yayyardımcı gibi genel kapsam tarafından kullanılabilirler :

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

Girdi verileri başlangıçta ile işlenir sed. Geçerli Yamlesque alanlarını bir ASCII Dosya Ayırıcı karakteri ile sınırlamadan ve değer alanını çevreleyen çift tırnak işaretlerini kaldırmadan önce Yamlesque biçim belirtimine uymayan satırları bırakır .

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

İki ifade benzerdir; sadece birincisi alıntılanan değerleri alır, ikincisi ise alıntılanmamış olanları alır.

İçerik Ayırıcı olmayan bir basılabilir karakter olarak, giriş verileri olması pek mümkün değildir, çünkü (28 / heks 12 / sekizlik 034) kullanılır.

Sonuç, awkgirdisini bir seferde bir satır işleyen borulara bağlanır. Her bir alanı bir değişkene atamak için FS karakterini kullanır :

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

Tüm satırların bir girintisi (muhtemelen sıfır) ve bir anahtarı vardır, ancak hepsinin bir değeri yoktur. Önde gelen boşluğu içeren ilk alanın uzunluğunu ikiye bölen satır için bir girinti düzeyi hesaplar. Girintisiz en üst düzey öğeler girinti sıfır seviyesinde.

Ardından, prefixgeçerli öğe için ne kullanılacağını belirler. Dizi adı yapmak için anahtar adına eklenen şey budur. Bir var root_prefixveri seti adı ve bir alt gibi tanımlanmıştır üst düzey dizisi için:

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

parent_keyGeçerli hattın girinti seviyesinin üzerinde girinti düzeyinde anahtar ve geçerli satır parçası olduğunu koleksiyonunu temsil eder. Koleksiyonunun anahtar / değer çiftleri arasında birleştirme olarak tanımlanır adını içeren bir dizi saklanır prefixve parent_key.

Üst seviye için (sıfır seviye girintisi), veri kümesi öneki üst anahtar olarak kullanılır, böylece öneki yoktur (olarak ayarlanır ""). Diğer tüm dizilere kök öneki eklenir.

Ardından, geçerli anahtar, anahtarları içeren (awk-internal) bir diziye eklenir. Bu dizi tüm awk oturumu boyunca devam eder ve bu nedenle önceki satırlar tarafından eklenen anahtarlar içerir. Anahtar, dizi dizini olarak girintisi kullanılarak diziye eklenir.

keys[indent] = key;

Bu dizi önceki satırlardaki anahtarları içerdiğinden, geçerli satırın girinti seviyesinden daha yüksek bir girinti düzeyi rende olan tüm anahtarlar kaldırılır:

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

Bu, anahtar zincirini içeren keys dizisini 0 girinti düzeyinde kökten geçerli satıra bırakır. Önceki satır geçerli satırdan daha derin girintili olduğunda kalan eski anahtarları kaldırır.

Son bölüm bashkomutları verir: değeri olmayan bir girdi satırı yeni bir girinti düzeyi başlatır ( YAML parlance'de bir koleksiyon ) ve değeri olan bir girdi satırı geçerli koleksiyona bir anahtar ekler.

Koleksiyonun adı geçerli satır en birleşimidir prefixve parent_key.

Bir anahtarın değeri olduğunda, geçerli koleksiyona şu değere sahip bir anahtar atanır:

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

İlk ifade, değeri anahtardan sonra adlandırılan ilişkilendirilebilir bir dizi öğesine atama komutunu verir ve ikincisi anahtarı koleksiyonun alanla sınırlandırılmış keyslistesine ekleme komutunu verir :

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

Bir anahtarın değeri yoksa, şu şekilde yeni bir koleksiyon başlatılır:

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

İlk ifade, yeni koleksiyonu geçerli koleksiyonun alanla sınırlandırılmış childrenlistesine ekleme komutunu verir ve ikincisi yeni koleksiyon için yeni bir ilişkilendirilebilir dizi bildirme komutunu verir:

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

yay_parseKaynağının tümü bash evalveya sourceyerleşik komutlar tarafından bash komutları olarak ayrıştırılabilir .


Bunu GitHub'da bir proje yapmayı düşündünüz mü? Yoksa zaten mi?
daniel

@daniel, GitHub'da ama kendi deposunda değil - burada bulabilirsiniz . examplesVe usr/libdizinlere bakın , Bunlar soruya cevabımla bağlantılıdır. Eğer ilgi varsa, onu kendi deposuna ayırabilirim.
starfry

4
Kudos YAY. İlk başta, onu saf bash olarak yeniden yazdım, ama sonra kendimi durduramadım ve birbirlerinin isimlerine basamayan diziler ve iç içe yapıları destekleyen temel bir ayrıştırıcı olarak yeniden uygulayamadım. Hiç de var github.com/binaryphile/y2s .
İkili Phile

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

sadece düz konfigürasyonlar için kullanışlıdır. yapılandırılmış yaml için geçerli değildir. başka bir, geçici file.sh kullanmayı önlemek için?
Znik

5

Başka bir seçenek de YAML'yi JSON'a dönüştürmektir, daha sonra JSON temsilcisiyle etkileşime girmek veya ondan bilgi almak veya düzenlemek için jq kullanın.

Bu tutkalı içeren basit bir bash betiği yazdım - GitHub'daki Y2J projesine bakın


2

Tek bir değere ihtiyacınız varsa, YAML belgenizi JSON'a dönüştüren ve jqörneğin besleyen bir araç kullanabilirsiniz yq.

Sample.yaml içeriği:

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

Misal:

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

1

Bunun çok spesifik olduğunu biliyorum, ancak cevabımın bazı kullanıcılar için yararlı olabileceğini düşünüyorum.
Eğer varsa nodeve npmmakinenizde yüklü, kullanabilirsiniz js-yaml.
İlk kurulum:

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

o zaman bash metninizde

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

Ayrıca kullanıyorsanız jqböyle bir şey yapabilirsiniz

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

Çünkü js-yamlbir yaml dosyasını json string değişmezine dönüştürür. Daha sonra dizeyi unix sisteminizdeki herhangi bir json ayrıştırıcı ile kullanabilirsiniz.


1

Python 2 ve PyYAML'iniz varsa, parse_yaml.py adlı yazdığım bu ayrıştırıcıyı kullanabilirsiniz . Yaptığı daha temiz şeylerden bazıları bir önek seçmenize (benzer değişkenlere sahip birden fazla dosyanız olması durumunda) ve bir yaml dosyasından tek bir değer seçmenize izin verir.

Örneğin, bu yaml dosyalarınız varsa:

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

Her ikisini de çakışma olmadan yükleyebilirsiniz.

$ 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

Ve kiraz bile istediğiniz değerleri seçer.

$ 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

Bir kullanabilirsiniz eşdeğer bir yQ golang yazılır:

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

İadeler:

62.0.3

0

Grunt'u (JavaScript Görev Çalıştırıcısı) kullanmayı da düşünebilirsiniz . Kabuk ile kolayca entegre edilebilir. YAML ( grunt.file.readYAML) ve JSON ( grunt.file.readJSON) dosyalarını okumayı destekler .

Bu, Gruntfile.js(veya Gruntfile.coffee) içinde bir görev oluşturarak gerçekleştirilebilir , örneğin:

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':
        });
    });

};

sonra kabuk sadece basitçe çalıştırın grunt foo( grunt --helpmevcut görevleri kontrol edin ).

Dahası , çıktıyı istediğiniz biçimde yazdırmak için , görevinizden ( ) girdi değişkenleri geçirilmiş exec:foogörevleri ( grunt-exec) uygulayabilir foo: { cmd: 'echo bar <%= foo %>' }ve ardından başka bir komuta ekleyebilirsiniz.


, Denir Grunt benzer bir araç da vardır yudum ek eklentisi ile yudum-YAML .

Yükleme yöntemi: npm install --save-dev gulp-yaml

Örnek kullanım:

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 formatıyla ilgilenmek için daha fazla seçenek için , YAML sitesini bu formatı ayrıştırmanıza yardımcı olabilecek mevcut projeler, kütüphaneler ve diğer kaynaklar için kontrol edin .


Diğer Aletler:

  • Jshon

    JSON'u ayrıştırır, okur ve oluşturur


0

Cevabımın spesifik olduğunu biliyorum, ancak zaten PHP ve Symfony yüklü ise, Symfony'nin YAML ayrıştırıcısını kullanmak çok kullanışlı olabilir.

Örneğin:

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

Burada sadece var_dumpayrıştırılmış dizinin çıktısını alırdım ama elbette çok daha fazlasını yapabilirsiniz ... :)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.