मैं लिनक्स शेल स्क्रिप्ट से एक YAML फ़ाइल कैसे पार्स कर सकता हूं?


193

मैं एक संरचित कॉन्फ़िगरेशन फ़ाइल प्रदान करना चाहता हूं जो कि गैर-तकनीकी उपयोगकर्ता के लिए संपादित करना जितना आसान हो सके (दुर्भाग्य से यह एक फ़ाइल होना है) और इसलिए मैं YAML का उपयोग करना चाहता था। हालाँकि मुझे इसे यूनिक्स शेल स्क्रिप्ट से पार्स करने का कोई तरीका नहीं मिल रहा है।


नहीं आपके सवाल का सीधे, लेकिन आप ansible को देखने के लिए अगर आपके खोल scriting विशेष रूप से अलग नोड्स (और एक YAML सूची) के दूरस्थ प्रबंधन के साथ काम कर के बारे में है चाहते हो सकता है
Eckes

9
yqशेल में yaml फ़ाइलों को पढ़ने / लिखने के लिए उपयोग करने का प्रयास करें । परियोजना पृष्ठ यहां है: mikefarah.github.io/yq आप उपकरण को बाइनरी के साथ स्थापित कर सकते हैं brew, aptया डाउनलोड कर सकते हैं। मान पढ़ना उतना ही सरल हैyq r some.yaml key.value
vdimitrov

@kenorb JSON = YML / YAML!
दुर्भाग्य

मैंने बारीकी से संबंधित कार्यों को पाया pkuczynski का गितुब , जिसमें से सबसे अच्छा (मेरे लिए) यह था कि जैस्पर की ओर से, अपने ही
गितुब

जवाबों:


56

मेरा उपयोग मामला काफी हो सकता है या नहीं भी हो सकता है क्योंकि यह मूल पोस्ट क्या पूछ रहा था, लेकिन यह निश्चित रूप से समान है।

मैं कुछ YAML में बैश चर के रूप में खींचने की जरूरत है। YAML कभी भी एक से अधिक गहरे स्तर की नहीं होगी।

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पाता :और यह जगह के साथ =", जबकि अनदेखी ://(यूआरएल के लिए)
  • s/$/"/g"प्रत्येक पंक्ति के अंत में जोड़ता है
  • s/ *=/=/g पहले सभी रिक्त स्थान निकाल देता है =

13
सुनिश्चित नहीं हैं कि आप क्या प्राप्त कर रहे हैं, लेकिन अगर आपका मतलब है कि यह सभी YAML के लिए काम नहीं करता है, तो आप सही हैं। इसलिए मैंने कुछ योग्यता के साथ खोला। मैंने केवल अपने उपयोग के मामले में क्या काम किया है, क्योंकि इसने उस समय किसी भी अन्य से बेहतर सवाल का जवाब दिया। यह निश्चित रूप से विस्तारित किया जा सकता है।
कर्टिस ब्लैकवेल

3
कोड इंजेक्शन के लिए भी थोड़ा सा खुला है, लेकिन जैसा कि आपने कहा कि एक कदम आगे है
Oriettaxx

1
मैंने केवल स्थानीय रूप से उपयोग करने के लिए शेल स्क्रिप्ट लिखी हैं, इसलिए यह मेरे लिए चिंता का विषय नहीं है। हालाँकि, यदि आप जानते हैं कि इसे कैसे सुरक्षित किया जाए और / या विस्तृत करना चाहते हैं, तो मैं निश्चित रूप से आभारी रहूँगा।
कर्टिस ब्लैकवेल

2
एक-स्तरीय-गहरे याम्ल के कई रूप हैं - मानों को निम्न इंडेंटेड लाइन में विभाजित किया जा सकता है; मूल्यों को कई तरीकों से उद्धृत किया जा सकता है जो शेल पार्स नहीं करेगा; सब कुछ ब्रेसिज़ के साथ एक पंक्ति पर लिखा जा सकता है {KEY: 'value', ...}:; और संभवतः अन्य। सबसे महत्वपूर्ण बात, यदि आप परिणाम को शेल कोड के रूप में मूल्यांकन करना चाहते हैं, तो यह बहुत असुरक्षित होगा।
बेनी चेर्नियाव्स्की-पास्किन

281

यहाँ एक बैश-केवल पार्सर है जो सरल याम्ल फ़ाइलों को पार्स करने के लिए sed और awk का लाभ उठाता है:

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"

यह रूबी द्वारा उत्पन्न 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

एक और अच्छा उपयोग पहले एक चूक फाइल को पार्स करने के लिए है और फिर उपयोगकर्ता सेटिंग्स, जो बाद में काम करता है पहली सेटिंग ओवरराइड करता है:

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

3
कूल स्टीफन! यह आश्चर्यजनक होगा यदि यह यम -संकेतन को देशी बैश सरणियों में भी बदल सकता है!
क्विकशिफ्टिन

3
यदि आप awk स्क्रिप्ट में प्रिंटफ लाइन को बदलते हैं, तो यह करना काफी आसान होना चाहिए। ध्यान दें कि बैश में बहुआयामी साहचर्य सरणियों के लिए समर्थन नहीं है, इसलिए आप एक सरणी + प्रति मूल्य की एक एकल कुंजी के साथ समाप्त होते हैं। हम्म, शायद इसे
गीथुब में

5
यह 2 स्थानों के मानक yml इंडेंटेशन की उम्मीद करता है। आप 4 स्थानों का उपयोग कर रहे हैं, तो चर परिसीमक के रूप में दो अंडरस्कोर मिल जाएगा, जैसे global__debugबजाय global_debug
k0pernikus

3
हाय वाब - जबकि मुझे यकीन है कि आप सही हैं कि कई पाठक शेल से वास्तविक YAML फ़ाइलों को पार्स करना चाहेंगे, यह बिल्कुल स्पष्ट नहीं है (कम से कम मेरे लिए) परिणाम क्या होगा। इस स्क्रिप्ट के साथ मैंने समस्या पर एक कड़ी पकड़ ली है और एक उपसमुच्चय को परिभाषित किया है जिसमें मानक चर में उचित मानचित्रण है। निश्चित रूप से वास्तविक YAML फ़ाइलों को पार्स करने के बड़े मुद्दे को संबोधित करने का कोई ढोंग नहीं है।
स्टीफन फारेस्टम

3
यह केवल स्क्रीन पर आउटपुट प्रिंट करता है। आप बाद में मूल्यों को कैसे एक्सेस करेंगे?
सत्तू

96

मैंने shyamlशेल कमांड लाइन से YAML क्वेरी की जरूरतों के लिए अजगर में लिखा है।

अवलोकन:

$ 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.-1का अंतिम तत्व है subvalue.things।)
  • सभी अनुक्रम / स्ट्रक्चर्स एलिमेंट तक पहुंच बैश लूप में उपयोग के लिए जाती है।
  • आप के रूप में एक YAML फ़ाइल के पूरे सब-आउटपुट कर सकते हैं ... YAML, जो श्याम के साथ आगे जोड़तोड़ के लिए अच्छी तरह से मिश्रण करता है।

अधिक नमूना और दस्तावेज़ीकरण श्याम गितुब पृष्ठ या श्याम पायपी पृष्ठ पर उपलब्ध हैं


1
यह कमाल का है! यदि उत्पादन में रिक्त यम मूल्यों को अनदेखा करने के लिए एक ध्वज था तो बहुत अच्छा होगा। अभी यह "अशक्त" आउटपुट करता है। मैं envdir के साथ इसका उपयोग करने के लिए एक dockercat docker-compose.yml | shyaml get-value api.environment | grep -v null | awk -F': ' '{print $2 > ("envdir/" $1)}'
compose

@JiminyCricket कृपया github समस्या पृष्ठ का उपयोग करें! मुझे कम से कम इस पर नज़र रखना खुशी होगी। ;)
vabab

1
दुर्भाग्य से, shyamlहास्यास्पद रूप से धीमा है
नोवोक्स

42

yq एक हल्का और पोर्टेबल कमांड-लाइन YAML प्रोसेसर है

परियोजना का उद्देश्य होना है JQ या YAML फाइल के sed।

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

एक उदाहरण के रूप में ( प्रलेखन से सीधे चुराया गया ), का एक नमूना दिया।

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

कुछ व्याख्याकारों के लिए एक छोटी स्क्रिप्ट को पारित करना संभव है, जैसे पायथन। रूबी और इसकी 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

, जहां datayaml से मानों के साथ एक हैश (या सरणी) है।

एक बोनस के रूप में, यह Jekyll के सामने की बात को ठीक-ठीक बताएगा।

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

1
क्या यह प्रयोग करने योग्य है? आप रूबी दुभाषिया को गूंज द्वारा yaml डाल दिया है। लेकिन बाकी वैश स्क्रिप्ट के तहत इस चर का उपयोग कैसे किया जाना चाहिए?
ज़नीक

हाँ, यह प्रयोग करने योग्य है। RUBY_SCRIPTचर एक गहरे लाल रंग का स्क्रिप्ट, जिसके कारण फ़ाइल बजाय (के साथ चलाने के लिए लिखा जा सकता है है ruby -ryaml <rubyscript_filename>)। इसमें इनपुट टेक्स्ट को कुछ आउटपुट टेक्स्ट में बदलने का तर्क है, आंतरिक रूप से सामग्री को dataचर में संग्रहीत करना । इको एक याम्ल पाठ को आउटपुट करता है, लेकिन आप cat <yaml_filename>इसके बजाय किसी फ़ाइल की सामग्री को पाइप करने के लिए उपयोग कर सकते हैं ।
राफेल

मुझे खेद है, लेकिन मैं इसे ऊपर के उदाहरण में नहीं देखता। पहले वेरिएबल में RUBY_SCRIPT माणिक दुभाषिया के लिए कोड रखता है। अगली इको-ई किसी भी याम्ल डेटा का अनुकरण करती है, यह पाइल द्वारा रूबी दुभाषिया में पुनर्निर्देशित होती है। यह रूबी कोड को इनलाइन स्क्रिप्ट के रूप में कहता है और अंत में आउटपुट उदाहरण 'a' और 'b' वेरिएबल्स पर प्रिंट करता है। फिर अपने शेष निष्पादन योग्य कोड के लिए बैश में परिवर्तनीय लोडिंग कहाँ है? मुझे केवल एक वर्कअराउंड दिखाई देता है। रूबी आउटआउट को अस्थायी_फाइल में डालते हुए, जिसे लाइनों को बदलना चाहिए: चर = 'मान', और उसके बाद इसे 'bash' में लोड करें। temporary_file '। लेकिन यह समाधान है, संकल्प नहीं।
ज़नीक

1
एक बार जब आप स्टडआउट पर कुछ प्राप्त कर लेते हैं, तो स्टड के साथ खिलाई गई चीज, बाकी बैश कोडर के हाथों में (और एक अनुस्मारक के रूप में, अगर आपको stdoutचर में खिलाए जाने की आवश्यकता है, तो आपको भरोसा करने की जरूरत नहीं है। अस्थायी फ़ाइलें! उपयोग x=$(...)या भी read a b c < <(...))। तो, यह एक वैध समाधान है जब आप जानते हैं कि आप वास्तव में YAML फ़ाइल में क्या लाना चाहते हैं और जानते हैं कि इस डेटा तक पहुंचने के लिए रूबी लाइनें कैसे लिखनी हैं। यहां तक ​​कि अगर यह किसी न किसी तरह है, यह IMHO विचार की एक पूर्ण सबूत है। फिर भी यह सच है कि यह आपको पूरी तरह से हरामीपन प्रदान नहीं करता है।
vabab

हाँ यही है। तुम कठोर हो। आपको उस चाल के लिए धन्यवाद। एक चर का प्रयोग सरल है। लेकिन कई युद्धबल नहीं हैं। चाल के साथ पढ़ने चर सूची <<(stdout के लिए निष्पादन) बहुत उपयोगी है :)
8

23

यह देखते हुए कि पायथन 3 और पायमामल आजकल मिलने के लिए काफी आसान निर्भरता हैं, निम्नलिखित मदद कर सकते हैं:

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

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

मुझे श्यामले बहुत पसंद हैं, लेकिन डिस्कनेक्टेड सिस्टम पर यह एक जीवन रक्षक है। Python2 के विशाल बहुमत के साथ ही काम करना चाहिए, जैसे, आरएचईएल।
rsaw

2
शायद yaml.safe_loadयह सुरक्षित है के रूप में उपयोग करें । pyyaml.org/wiki/PyYAMLDocumentation
जॉर्डन स्टीवर्ट

14

यहां स्टीफन फारेस्टम के उत्तर का एक विस्तारित संस्करण दिया गया है:

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कोई बहुआयामी सरणियाँ नहीं हैं, इसलिए यह काम करने का एक तरीका है। कई स्तरों का समर्थन किया जाता है। @Briceburg द्वारा उल्लिखित श्वेत रिक्त स्थान के साथ समस्या के आसपास काम करने के लिए एकल या दोहरे उद्धरण चिह्नों में मानों को संलग्न करना चाहिए। हालांकि, अभी भी कुछ सीमाएँ हैं: शब्दकोशों और सूचियों का विस्तार गलत परिणाम उत्पन्न कर सकता है जब मान कॉमा होते हैं। इसके अलावा, अधिक जटिल संरचनाएं जैसे कि कई लाइनें (जैसे ssh- कीज़) फैले हुए मान (अभी तक) समर्थित नहीं हैं।

कोड के बारे में कुछ शब्द: पहला sedकमांड { key: value, ...}नियमित रूप से शब्दकोशों के संक्षिप्त रूप का विस्तार करता है और उन्हें अधिक सरल यमल शैली में परिवर्तित करता है। दूसरी sedकॉल सूचियों के संक्षिप्त संकेतन के लिए ही करती है और संकेतन के [ entry, ... ]साथ एक आइटम सूची में परिवर्तित हो जाती -है। तीसरी sedकॉल मूल एक है जो सामान्य शब्दकोशों को संभाला करती है, अब सूचियों को -इंडेंटेशन के साथ संभालती है । awkभाग (यानी एक सूची पर संसाधित करने) जब चर का नाम रिक्त है प्रत्येक खरोज स्तर है और यह बढ़ जाती है के लिए एक सूचकांक परिचय देता है। खाली vname के बजाय काउंटरों के वर्तमान मूल्य का उपयोग किया जाता है। एक स्तर ऊपर जाने पर, काउंटर शून्य हो जाते हैं।

संपादित करें: मैंने इसके लिए एक github रिपॉजिटरी बनाई है ।


11

यह कहना मुश्किल है क्योंकि यह निर्भर करता है कि आप पार्सर को अपने YAML दस्तावेज़ से क्या निकालना चाहते हैं। साधारण मामलों के लिए, आप उपयोग करने में सक्षम हो सकता है grep, cut, awkऔर अधिक जटिल पार्स आप का उपयोग करने की आवश्यकता होगी के लिए आदि एक पूर्ण विकसित पायथन के रूप में पुस्तकालय ऐसे पार्स करने PyYAML या YAML :: पर्ल


11

मैंने सिर्फ एक पार्सर लिखा था जिसे मैंने याय कहा था ! ( यम्ल यमलेसक नहीं है! ) जो यमलेस , यमशेल का एक छोटा उपसमुच्चय है। तो, अगर आप बैश के लिए 100% आज्ञाकारी YAML पार्सर की तलाश कर रहे हैं तो यह नहीं है। हालांकि, ओपी को उद्धृत करने के लिए, यदि आप एक संरचित कॉन्फ़िगरेशन फ़ाइल चाहते हैं जो कि गैर-तकनीकी उपयोगकर्ता के लिए जितना संभव हो उतना आसान है कि वह YAML की तरह हो, तो यह रुचि का हो सकता है।

यह पहले वाले उत्तर से हैरान है लेकिन मूल चर के बजाय साहचर्य सरणियों को लिखता है ( हाँ, इसके लिए बैश 4.x की आवश्यकता है )। यह एक तरह से ऐसा करता है जो डेटा को चाबियों के पूर्व ज्ञान के बिना पार्स करने की अनुमति देता है ताकि डेटा-संचालित कोड लिखा जा सके।

कुंजी / मान सरणी तत्वों के साथ-साथ, प्रत्येक सरणी में एक keysसरणी होती है जिसमें प्रमुख नामों की सूची होती है, एक childrenसरणी जिसमें बाल सरणियों के नाम और एक parentकुंजी होती है जो अपने माता-पिता को संदर्भित करती है।

यह यमलेस्क का एक उदाहरण है:

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) जो बैश संस्करण की एक विशेषता है। घोषणाएँ भी वैश्विक हैं ( -g) इसलिए उन्हें एक फ़ंक्शन में निष्पादित किया जा सकता है लेकिन yayसहायक जैसे वैश्विक दायरे में उपलब्ध हो सकता है :

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

इनपुट डेटा शुरू में साथ संसाधित किया जाता है sed। यह ऐसी लाइनें छोड़ता है जो ASCII फ़ाइल सेपरेटर कैरेक्टर के साथ वैध यमलेकस फ़ील्ड्स को डिलिमिट करने से पहले यमलेस फॉर्मेट स्पेसिफिकेशन से मेल नहीं खातीं और वैल्यू फील्ड के आसपास के किसी भी डबल-कोट्स को हटा देती हैं।

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

दो भाव समान हैं; वे केवल इसलिए भिन्न हैं क्योंकि पहले वाले ने उद्धृत मूल्यों को चुना, जहां दूसरे ने अनछुए लोगों को चुना।

फ़ाइल सेपरेटर (28 / हेक्स 12 / अष्टाधारी 034), क्योंकि एक प्रिंट न हो सकने चरित्र के रूप में, यह इनपुट डेटा में होने की संभावना नहीं है प्रयोग किया जाता है।

परिणाम को पाइप किया जाता है awkजो एक बार में अपने इनपुट को एक पंक्ति में संसाधित करता है। यह प्रत्येक क्षेत्र को एक चर में नियत करने के लिए FS वर्ण का उपयोग करता है :

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

सभी लाइनों में एक इंडेंट (संभवतः शून्य) और एक कुंजी होती है, लेकिन इन सभी का कोई मूल्य नहीं होता है। यह पहली फ़ील्ड की लंबाई को विभाजित करने वाली रेखा के लिए एक इंडेंट स्तर की गणना करता है, जिसमें दो के द्वारा अग्रणी व्हाट्सएप होता है। बिना किसी इंडेंट के शीर्ष स्तर के आइटम इंडेंट स्तर शून्य पर हैं।

इसके बाद, यह काम करता है कि 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

शीर्ष स्तर (इंडेंट स्तर शून्य) के लिए डेटा सेट उपसर्ग का उपयोग मूल कुंजी के रूप में किया जाता है, इसलिए इसमें कोई उपसर्ग नहीं है (यह सेट है "")। अन्य सभी सरणियों को मूल उपसर्ग के साथ उपसर्ग किया जाता है।

इसके बाद, वर्तमान कुंजी को एक (awk-internal) सरणी में डाला जाता है जिसमें कुंजियाँ होती हैं। यह सरणी पूरे awk सत्र में बनी रहती है और इसलिए इसमें पूर्व लाइनों द्वारा डाली गई कुंजियाँ होती हैं। सरणी इंडेक्स के रूप में इंडेंट का उपयोग करके कुंजी को सरणी में डाला जाता है।

keys[indent] = key;

क्योंकि इस सरणी में पिछली पंक्तियों की कुंजियाँ शामिल हैं, वर्तमान पंक्ति के इंडेंट स्तर की तुलना में इंडेंट स्तर ग्रेटर वाली किसी भी कुंजी को हटा दिया जाता है:

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

यह की-चेन को की-चेन से रूट से इंडेंट लेवल पर करंट लाइन से करंट लाइन पर छोड़ता है। यह बासी कुंजियों को हटा देता है जो तब बनी रहती हैं जब पूर्व की रेखा वर्तमान लाइन की तुलना में अधिक गहरी होती थी।

अंतिम खंड bashआदेशों को आउटपुट करता है : मूल्य के बिना एक इनपुट लाइन एक नया इंडेंट स्तर ( YAML parlance में एक संग्रह ) और मूल्य के साथ एक इनपुट लाइन वर्तमान संग्रह के लिए एक कुंजी जोड़ता है।

संग्रह का नाम वर्तमान पंक्ति के संयोजन है prefixऔर parent_key

जब किसी कुंजी का मान होता है, तो उस मान के साथ एक कुंजी इस तरह वर्तमान संग्रह को सौंपी जाती है:

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

पहला कथन कुंजी के बाद नामित एक साहचर्य सरणी तत्व को मान असाइन करने के लिए कमांड को आउटपुट करता है और दूसरा संग्रह के स्थान-सीमांकित 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सूची में नए संग्रह को जोड़ने के लिए आदेश को आउटपुट करता है और दूसरा आदेश नए संग्रह के लिए एक नया साहचर्य सरणी घोषित करने के लिए आदेश को आउटपुट करता है:

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

से सभी आउटपुट yay_parseको bash evalया sourceबिल्ट-इन कमांड द्वारा bash कमांड के रूप में पार्स किया जा सकता है ।


क्या आपने इसे GitHub पर एक परियोजना बनाने पर विचार किया है? या यह पहले से ही है?
daniel

@daniel, यह GitHub में है लेकिन अपने स्वयं के रेपो में नहीं - आप इसे यहां पा सकते हैं । examplesऔर usr/libनिर्देशिकाओं को देखें , ये प्रश्न के मेरे उत्तर से जुड़े हैं। अगर मेरी रुचि है तो मैं इसे अपने ही रेपो में तोड़ सकता हूं।
स्टारफ्री

4
YAY पर कुदोस। सबसे पहले, मैंने इसे शुद्ध बैश होने के लिए फिर से लिखा था, लेकिन फिर मैं खुद को रोक नहीं सका और एरे और नेस्टेड संरचनाओं के समर्थन के साथ एक मूल पार्सर के रूप में इसे फिर से लागू किया जो एक दूसरे के नाम पर कदम नहीं रख सकते। यह github.com/binaryphile/y2s पर है
बाइनरी फाइल

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

केवल समतल विन्यास के लिए उपयोगी है। यह संरचित यमल के लिए लागू नहीं है। एक और, कैसे अस्थायी file.sh का उपयोग करने से रोकने के लिए?
ज़नीक

5

एक अन्य विकल्प यह है कि YAML को JSON में परिवर्तित करें, फिर JSON प्रतिनिधित्व के साथ बातचीत करने के लिए jq का उपयोग करें या तो इसमें से जानकारी निकालें या इसे संपादित करें।

मैंने एक साधारण बैश स्क्रिप्ट लिखी जिसमें यह गोंद है - GitHub पर Y2J प्रोजेक्ट देखें


2

यदि आपको एकल मूल्य की आवश्यकता है तो आप एक उपकरण दे सकते हैं जो आपके YAML दस्तावेज़ को JSON में परिवर्तित करता है और jqउदाहरण के लिए फ़ीड करता है yq

नमूना की सामग्री। श्याम:

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

उदाहरण:

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

1

मुझे पता है कि यह बहुत विशिष्ट है, लेकिन मुझे लगता है कि मेरा उत्तर कुछ उपयोगकर्ताओं के लिए मददगार हो सकता है।
यदि आप अपने मशीन पर स्थापित nodeऔर npmस्थापित हैं, तो आप उपयोग कर सकते हैं js-yaml
पहले स्थापित करें:

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

फिर आपकी बैश स्क्रिप्ट में

#!/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एक यम फाइल को जोंस स्ट्रिंग शाब्दिक रूप से परिवर्तित करता है। फिर आप अपने यूनिक्स सिस्टम में किसी भी json पार्सर के साथ स्ट्रिंग का उपयोग कर सकते हैं।


1

यदि आपके पास अजगर 2 और PyYAML है, तो आप इस पार्सर का उपयोग कर सकते हैं जिसे मैंने लिखा है parse_yaml.py । कुछ ऐसी नटखट चीजें हैं जो आपको एक उपसर्ग चुनने देती हैं (यदि आपके पास समान चर वाली एक से अधिक फ़ाइल है) और एक याम्ल फ़ाइल से एक मान लेने के लिए।

उदाहरण के लिए यदि आपके पास ये 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

आप एक समकक्ष का उपयोग कर सकते हैं जो कि गोलंग में लिखा गया है:

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

रिटर्न:

62.0.3

0

आप ग्रंट (द जावास्क्रिप्ट टास्क रनर) का उपयोग करने पर भी विचार कर सकते हैं । आसानी से खोल के साथ एकीकृत किया जा सकता है। यह 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 foo( grunt --helpउपलब्ध कार्यों की जांच करें )।

आगे आप अपने कार्य ( ) से पारित इनपुट चर के साथ exec:fooकार्यों ( grunt-exec) को लागू कर सकते हैं foo: { cmd: 'echo bar <%= foo %>' }ताकि आप जो भी प्रारूप चाहते हैं उसमें आउटपुट प्रिंट कर सकें, फिर इसे किसी अन्य कमांड में पाइप करें।


ग्रंट के समान उपकरण भी है, इसे अतिरिक्त प्लग - इन gulp-yaml के साथ gulp कहा जाता है ।

इसके माध्यम से स्थापित करें: 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

मुझे पता है कि मेरा उत्तर विशिष्ट है, लेकिन अगर पहले से ही PHP और Symfony स्थापित है, तो 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पार्स सरणी का उत्पादन करने के लिए इस्तेमाल किया, लेकिन निश्चित रूप से आप बहुत अधिक कर सकते हैं ... :)

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.