Linux 쉘 스크립트에서 YAML 파일을 구문 분석하려면 어떻게해야합니까?


193

기술적으로 익숙하지 않은 사용자가 편집 할 수있는 구조화 된 구성 파일 (불행히도 파일이어야 함)을 제공하고 YAML을 사용하고 싶었습니다. 그러나 유닉스 쉘 스크립트에서 이것을 파싱하는 방법을 찾을 수 없습니다.


쉘 스크래 팅이 특히 다른 노드 (및 yaml 인벤토리)의 원격 관리를 다루는 것에 관한 것인지 궁금하다면 직접 질문 해보십시오.
eckes

9
yq쉘에서 yaml 파일을 읽거나 쓰는 데 사용하십시오 . 프로젝트 페이지는 여기에 있습니다 : mikefarah.github.io/yq 당신과 도구를 설치할 수 있습니다 brew, apt또는 바이너리를 다운로드합니다. 값을 읽는 것은 간단합니다yq r some.yaml key.value
vdimitrov

@kenorb JSON! = yml / YAML
swe

나는 밀접 기능을 관련 발견 pkuczynski의 GitHub의 (나를 위해) 최선가에서 그였다있는 jasperes의 자신의 GitHub의 유지
splaisan

답변:


56

내 유스 케이스는이 원래 게시물이 요청한 것과 완전히 같거나 같지 않을 수도 있지만 확실히 유사합니다.

bash 변수로 일부 YAML을 가져와야합니다. YAML은 한 레벨 이상이 될 수 없습니다.

YAML은 다음과 같습니다.

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

dis와 같은 출력 :

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에서 작동하지 않는다는 것을 의미한다면 맞습니다. 그래서 몇 가지 자격으로 개설했습니다. 방금 질문에 더 잘 대답했기 때문에 유스 케이스에 효과가 있었던 것을 공유했습니다. 이것은 확실히 확장 될 수 있습니다.
커티스 블랙웰

3
코드 인젝션에도 약간 개방적이지만, 당신이 말했듯이 단계적으로 진행
Oriettaxx

1
로컬에서 사용할 쉘 스크립트 만 작성 했으므로 걱정하지 않아도됩니다. 그러나 보안 방법 및 / 또는 정교화 방법을 알고 있다면 정말 감사하겠습니다.
커티스 블랙웰

2
1 단계 깊이의 yaml은 여러 형태를 가지고 있습니다. 값은 다음 들여 쓰기 된 줄로 나눌 수 있습니다. 쉘이 구문 분석하지 않는 여러 가지 방법으로 값을 인용 할 수 있습니다. 중괄호를 사용하여 한 줄에 모든 것을 쓸 수 있습니다. {KEY: 'value', ...}; 그리고 아마도 다른 사람들. 가장 중요한 것은 결과를 쉘 코드로 평가하려는 경우 매우 안전하지 않은 것입니다.
Beni Cherniavsky-Paskin

281

다음은 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"

또한 루비로 생성 된 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
쿨 스테판! yaml -표기법을 기본 배쉬 배열로 바꿀 수 있다면 놀랍 습니다!
quickshiftin

3
awk 스크립트에서 printf 행을 변경하면 쉽게 할 수 있습니다. bash는 다차원 연관 배열을 지원하지 않으므로 배열 + 값 당 단일 키로 끝납니다. 흠, 아마도 이것을 github으로 옮겨야 할 것입니다.
Stefan Farestam

5
이것은 2 칸의 표준 yml 들여 쓰기를 기대합니다. 4 개의 공백을 사용하는 경우 변수는 두 개의 밑줄을 구분 기호로 사용합니다 (예 : global__debug대신) global_debug.
k0pernikus

3
안녕 vaab-많은 독자들이 쉘에서 실제 YAML 파일을 구문 분석하고 싶다고 확신하지만 결과가 무엇인지는 분명하지 않습니다 (적어도 나에게는). 이 스크립트를 사용하여 문제를 찌르고 표준 변수에 합리적인 매핑을하는 하위 집합을 정의했습니다. 실제 YAML 파일을 파싱하는 더 큰 문제를 해결 한 것은 확실하지 않습니다.
Stefan Farestam

3
화면에 출력 만 인쇄합니다. 나중에 값에 어떻게 액세스 하시겠습니까?
sattu

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시퀀스 의 마지막 요소 입니다).
  • bash 루프에서 사용하기 위해 한 번에 모든 시퀀스 / 구조 요소에 액세스
  • YAML 파일의 전체 하위 부분을 YAML로 출력 할 수 있습니다.

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 이슈 페이지를 사용하십시오! 적어도 이것을 추적하는 것이 기쁠 것입니다. ;)
vaab

1
불행히도, shyaml엄청나게 느리다
nowox

43

yq 는 경량의 휴대용 명령 줄 YAML 프로세서입니다.

프로젝트의 목적은 yaq 파일 의 jq 또는 sed 가되는 것 입니다.

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

다음과 같은 sample.yaml 파일을 예로 들어 ( 문서 에서 직접 도난당한 )

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

그때

yq r sample.yaml bob.*.cats

출력합니다

- bananas
- apples

필터링 기능이 부족합니다
Antonin

formulae.brew.sh/formula/yq 는 지난해 26,679 개를 설치했습니다.
dustinevan

1
@Antonin 이것이 당신의 뜻인지 확실하지 않지만 현재 필터링 기능이있는 것 같습니다 : mikefarah.gitbook.io/yq/usage/path-expressions
bmaupin

32

파이썬과 같은 일부 인터프리터에게 작은 스크립트를 전달할 수 있습니다. 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

여기서 datayaml의 값이있는 해시 (또는 배열)입니다.

보너스로 Jekyll의 앞면 문제 를 잘 해석 할 수 있습니다.

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

1
사용할 수 있습니까? yaml을 echo로 ruby ​​인터프리터에 넣었습니다. 그러나 나머지 bash 스크립트 에서이 변수를 어떻게 사용해야합니까?
Znik

예, 사용할 수 있습니다. RUBY_SCRIPT변수는 파일 대신 (실행에 기록 될 수 있습니다 루비 스크립트입니다 ruby -ryaml <rubyscript_filename>). 입력 텍스트를 일부 출력 텍스트로 변환하여 내부적으로 컨텐츠를 data변수에 저장하는 로직이 포함되어 있습니다 . 에코는 yaml 텍스트를 출력하지만 cat <yaml_filename>대신 파일 내용을 파이프하는 데 사용할 수 있습니다 .
Rafael

죄송하지만 위의 예에서는 이것을 볼 수 없습니다. 처음에 변수 RUBY_SCRIPT는 루비 인터프리터를위한 코드를 유지합니다. 다음 echo -e는 모든 yaml 데이터를 시뮬레이트합니다. 이는 루비 인터프리터로 파일을 리디렉션하는 것입니다. 이것은 루비 코드를 인라인 스크립트로 호출하고 마지막으로 'a'및 'b'변수 예제를 출력하도록 인쇄합니다. 그렇다면 나머지 실행 코드를 위해 변수를 bash에 어디에로드합니까? 하나의 해결 방법 만 보입니다. ruby를 temporary_file에 넣고, variable = 'value'행을 가져 와서 'bash'에 의해 bash에로드합니다. temporary_file '. 그러나 이것은 해결 방법이 아니라 해결 방법입니다.
Znik

1
@ Znik은 stdin으로 공급 된 무언가에 의해 생성 된 stdout에 무언가를 얻었을 때 나머지는 bash 코더의 손에 의존합니다 (그리고 stdout변수에 공급 해야하는 경우 알림 에 의존 할 필요가 없습니다) 임시 파일! 사용 x=$(...)도 또는 read a b c < <(...)). 따라서 이것은 YAML 파일에서 가져올 것을 정확히 알고 있고 루비 라인을 작성하여이 데이터에 액세스하는 방법을 알고있을 때 유효한 솔루션입니다. 거칠더라도 IMHO 아이디어의 완전한 개념 증명입니다. 그럼에도 불구하고 완전한 배쉬 추상화를 제공하지는 않습니다.
vaab

네 그렇습니다. 당신은 비웃습니다. 그 트릭에 당신을 녹여. 하나의 변수를 사용하는 것은 간단합니다. 그러나 많은 사악한 것들이 아닙니다. 읽기 변수 목록이있는 트릭 <<(stdout으로 실행)은 매우 유용합니다 :)
Znik

23

Python3과 PyYAML은 현재 충족하기 쉬운 종속성이므로 다음이 도움이 될 수 있습니다.

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

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

나는 shyaml을 좋아하지만 연결이 끊어진 시스템에서 이것은 생명의 은인입니다. RHEL과 같이 대다수의 python2에서도 작동합니다.
rsaw

2
yaml.safe_load더 안전한 그대로 사용 하십시오. pyyaml.org/wiki/PyYAML 문서
Jordan Stewart

14

Stefan Farestam의 답변의 확장 버전은 다음과 같습니다.

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-key와 같은 여러 줄에 걸친 값과 같은 더 복잡한 구조는 (아직) 지원되지 않습니다.

코드에 대한 몇 마디 : 첫 번째 sed명령은 짧은 형식의 사전 { key: value, ...}을 일반으로 확장하고 더 간단한 yaml 스타일로 변환합니다. 두 번째 sed호출은 목록의 짧은 표기법에 대해서도 동일하게 수행되며 표기법이 [ entry, ... ]있는 항목 별 목록으로 변환 -됩니다. 세 번째 sed호출은 일반 사전을 처리 한 원래 호출이며 이제는 목록 -과 들여 쓰기 를 처리합니다 . 이 awk부분은 각 들여 쓰기 수준에 대한 색인을 도입하고 변수 이름이 비어있을 때 (예 : 목록을 처리 할 때) 색인을 증가시킵니다. 빈 vname 대신 카운터의 현재 값이 사용됩니다. 한 레벨 위로 올라가면 카운터가 0이됩니다.

편집 : 이것을 위해 github 저장소 를 만들었습니다 .


11

파서가 YAML 문서에서 추출하려는 대상에 따라 다르므로 말하기 어렵습니다. 간단한 경우를 들어, 사용할 수 있습니다 grep, cut, awk사용할 필요가 더 복잡한 구문 분석 등 본격적인 파이썬으로 라이브러리 등을 분석 PyYAML 또는 YAML :: 펄 .


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 File Separator 문자로 구분 하고 값 필드 주변의 큰 따옴표를 제거 하기 전에 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" |

두 표현은 비슷합니다. 첫 번째는 따옴표가없는 값을 선택하는 인용 된 값을 선택하기 때문에 다릅니다.

파일 분리기 비 인쇄 문자로, 입력 된 데이터에있을 가능성이 있기 때문 (28 / 12 진수 / 진수 034)이 사용된다.

결과는 awk한 번에 한 줄씩 입력을 처리하는 파이프로 연결됩니다 . FS 문자를 사용하여 각 필드를 변수에 지정합니다.

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

모든 줄에는 들여 쓰기 (0으로 표시)와 키가 있지만 모두 값이있는 것은 아닙니다. 선행 공백을 포함하는 첫 번째 필드의 길이를 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현재 라인의 덴트 레벨 위 오목 레벨의 키이며, 현재 라인의 일부 콜렉션을 나타낸다. 컬렉션의 키 / 값 쌍은 prefixand 의 연결로 정의 된 이름을 가진 배열에 저장됩니다 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);

첫 번째 명령문은 키의 이름을 지정한 연관 배열 요소에 값을 지정하는 명령을 출력하고 두 번째 명령문은 키를 콜렉션의 공백으로 구분 된 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, 그것은 GitHub에 있지만 자체 저장소에는 없습니다 . 여기 에서 찾을 수 있습니다 . examplesusr/lib디렉토리를 참조하십시오 .이 질문에 대한 답변에 링크되어 있습니다. 관심이 있다면 나는 그것을 자신의 저장소로 나눌 수 있습니다.
starfry

4
예요. 처음에는 순수한 bash로 다시 작성했지만, 나는 스스로를 멈추고 서로의 이름을 밟을 수없는 중첩 구조와 배열을 지원하는 기본 파서로 다시 구현할 수 없었습니다. 그것은에서의 github.com/binaryphile/y2s .
이진 Phile

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

평면 구성에만 유용합니다. 구조화 된 yaml에는 적용되지 않습니다. 또 다른 방법으로 임시 file.sh 사용을 방지하는 방법은 무엇입니까?
Znik

5

다른 옵션은 YAML을 JSON으로 변환 한 다음 jq를 사용하여 JSON 표현과 상호 작용하여 정보를 추출하거나 편집하는 것입니다.

이 접착제가 포함 된 간단한 bash 스크립트를 작성했습니다 -GitHub의 Y2J 프로젝트 참조


2

단일 값이 필요한 경우 jq예를 들어 YAML 문서를 JSON으로 변환하고 피드하는 도구를 사용할 수 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변환 JSON 문자열 리터럴에 YAML 파일. 그런 다음 유닉스 시스템에서 json 파서와 함께 문자열을 사용할 수 있습니다.


1

당신이 파이썬 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

당신은 사용할 수 있습니다 상당YQ golang 기록한다 :

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

보고:

62.0.3

0

Grunt (JavaScript Task Runner) 사용을 고려할 수도 있습니다 . 쉘과 쉽게 통합 할 수 있습니다. 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 %>' }한 다음 다른 명령으로 파이프 할 수 있습니다.


Grunt와 유사한 도구가 있으며, 추가 플러그인 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 사이트 에서 사용 가능한 프로젝트, 라이브러리 및 해당 형식을 구문 분석하는 데 도움이되는 기타 리소스를 확인하십시오 .


다른 도구들 :

  • hon

    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.