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 keys
anahtar ad listesi children
, alt dizilerin adlarını içeren bir dizi parent
ve ü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.
bash
Yü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 -A
Bash 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 yay
yardı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ç, awk
girdisini 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, prefix
geçerli öğe için ne kullanılacağını belirler. Dizi adı yapmak için anahtar adına eklenen şey budur. Bir var root_prefix
veri 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_key
Geç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 prefix
ve 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 bash
komutları 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 prefix
ve 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ış keys
listesine 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ış children
listesine 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_parse
Kaynağının tümü bash eval
veya source
yerleşik komutlar tarafından bash komutları olarak ayrıştırılabilir .