Ali*_*aka 9 bash shell-script associative-array
解析内容的细节:散列和等号。其他一切都是奖金。
属性文件内容示例:
# comment
a=value-a
b=http://prefix.suffix:8080/?key=value
c=password_with\\backslash-and=equals
Run Code Online (Sandbox Code Playgroud)
我希望从该文件中构造这个 bash 关联数组:
declare -A props='(
[a]="value-a"
[b]="http://prefix.suffix:8080/?key=value"
[c]="password_with\\backslash-and=equals" )'
Run Code Online (Sandbox Code Playgroud)
(declare -p该关联数组的预期输出,注意${props[c]}只包含一个反斜杠,"\\"是'\')。
使用真正的解析器,如perl的Config::Properties模块。我会在 中完成整个脚本perl,但是如果您必须使用bash,您可以执行以下操作:
typeset -A props
while IFS= read -rd '' key && IFS= read -rd '' value; do
props[$key]=$value
done < <(
perl -MConfig::Properties -l0 -e '
$p = Config::Properties->new();
$p->load(STDIN);
print for $p->properties' < file.properties
)
Run Code Online (Sandbox Code Playgroud)
(也适用于zsh)。
实现一个完整的解析器bash需要大量的工作,意味着重新发明轮子。您可以使用简单的while read循环来实现一个好的子集,因为read内置函数需要与这些属性文件非常相似的输入格式:
typeset -A props
while IFS=$':= \t' read key value; do
[[ $key = [#!]* ]] || [[ $key = "" ]] || props[$key]=$value
done < file.properties
Run Code Online (Sandbox Code Playgroud)
(也适用于ksh93和zsh,另外两个支持关联数组的类似 Bourne 的 shell)。
处理:
prop = valueprop: valueprop value!以及#可选的前导空格)foo\:\:bar=value含有分隔符或钥匙foo=\ bar或password_with\\backslash-and=equals你的样品中)。但是,如果我们检查规范
那不处理\n, \r, \uXXXX... 序列
LF 是唯一可识别的行分隔符(不是 CR 也不是 CRLF)。
FF 不被识别为空格(我们不能仅仅$IFS根据 shell 和版本将其添加到其中,\f不一定会被识别为 IFS 空格字符¹)。
对于像 那样的输入foo: bar = ,它存储bar在${props[foo]}而不是bar = (foo: bar:baz:虽然可以)。只有当属性的值包含一个(未转义的)定界符(:可选地由 SPC/TAB 字符=包围,可选地由 SPC/TAB 字符或一个或多个 SPC/TAB 字符的序列包围)并且位于末尾时,这才是一个问题。
它被视为以\!or开头的注释行\#。只有名称以!or开头的属性才有问题#。
在
prop=1\
2\
3
Run Code Online (Sandbox Code Playgroud)
我们得到1 2 3而不是123:连续行中的前导空格不会被忽略,因为它们应该是。
² IFS 空白字符,每个 POSIX 是[:space:]在语言环境中分类的字符(通常包括\f但不必),并且碰巧$IFS在 ksh88(POSIX 规范所基于的)和大多数 shell 中,那是仍然限于 SPC、TAB 和 NL。我发现在这方面唯一符合 POSIX 的 shell 是yash. ksh93并且bash(从 5.0 开始)还包括其他空格(例如 CR、FF、VT...),但仅限于单字节(在某些系统上(例如 Solaris)要小心,其中包括单字节的不间断空格在某些地区)
这是 Bash4+ 中的操作方法
#!/usr/bin/env bash
declare -A properties
# Read with:
# IFS (Field Separator) =
# -d (Record separator) newline
# first field before separator as k (key)
# second field after separator and reminder of record as v (value)
while IFS='=' read -d $'\n' -r k v; do
# Skip lines starting with sharp
# or lines containing only space or empty lines
[[ "$k" =~ ^([[:space:]]*|[[:space:]]*#.*)$ ]] && continue
# Store key value into assoc array
properties[$k]="$v"
# stdin the properties file
done < file.properties
# display the array for testing
typeset -p properties
Run Code Online (Sandbox Code Playgroud)
file.properties:
# comment
a=value-a
b=http://prefix.suffix:8080/?key=value
c=password_with\\backslash-and=equals
d e=the d e value
# comment
Run Code Online (Sandbox Code Playgroud)
来自提供的数据样本的此脚本的输出:
declare -A properties=(["d e"]="the d e value" [c]="password_with\\\\backslash-and=equals" [b]="http://prefix.suffix:8080/?key=value" [a]="value-a" )
Run Code Online (Sandbox Code Playgroud)
declare -A properties
function readPopertyFile
{
while read line || [[ -n $line ]]; do
key=`echo $line | cut -s -d'=' -f1`
if [ -n "$key" ]; then
value=`echo $line | cut -d'=' -f2-`
properties["$key"]="$value"
fi
done < $1
}
Run Code Online (Sandbox Code Playgroud)
用法:
readPopertyFile "file.properties"
Run Code Online (Sandbox Code Playgroud)
将把属性读入名为 的关联数组变量中properties。
* 在 bash 中工作。不知道其他的贝壳怎么样。
* 不会处理多行属性。