방금 내가 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
현재 라인의 덴트 레벨 위 오목 레벨의 키이며, 현재 라인의 일부 콜렉션을 나타낸다. 컬렉션의 키 / 값 쌍은 prefix
and 의 연결로 정의 된 이름을 가진 배열에 저장됩니다 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 명령으로 구문 분석 될 수 있습니다 .