我想读取 a 中的属性属性文件到关联数组中。我该怎么做?
有关解析内容的详细信息:哈希和等号。其他一切都是奖金。
属性文件内容示例:
# comment
a=value-a
b=http://prefix.suffix:8080/?key=value
c=password_with\\backslash-and=equals
我希望从该文件构造这个 bash 关联数组:
declare -A props='(
[a]="value-a"
[b]="http://prefix.suffix:8080/?key=value"
[c]="password_with\\backslash-and=equals" )'
declare -p
(该关联数组的预期输出,请注意,${props[c]}
仅包含一个反斜杠,"\\"
即'\'
)。
答案1
使用真正的解析器,例如perl
'sConfig::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
)
(也适用于zsh
)。
实现一个完整的解析器bash
需要大量工作,并且意味着重新发明轮子。您可以使用简单的循环来实现一个好的子集while read
,因为read
内置函数需要与这些属性文件非常相似的输入格式:
typeset -A props
while IFS=$':= \t' read key value; do
[[ $key = [#!]* ]] || [[ $key = "" ]] || props[$key]=$value
done < file.properties
(也可以与ksh93
和 一起使用zsh
,这是另外两个支持关联数组的类似 Bourne 的 shell)。
这处理:
prop = value
prop: value
prop value
- 注释位于行首(
!
并且#
带有可选的前导空格) - 反斜杠转义(如示例中包含分隔符或的
foo\:\:bar=value
键)。foo=\ bar
password_with\\backslash-and=equals
- 带反斜杠的续行
但是,如果我们检查规格
这不处理
\n
,\r
,\uXXXX
... 序列LF 是唯一可识别的行分隔符(不是 CR 也不是 CRLF)。
FF 不被识别为空白(我们不能只是将其添加到
$IFS
as ,具体取决于 shell 和版本,\f
不一定会被识别为 IFS 空白字符 1)。对于像 之类的输入
foo: bar =
,存储bar
in${props[foo]}
而不是bar =
(foo: bar:baz:
不过也可以)。仅当属性值包含一个(未转义的)分隔符(:
可选地由 SPC/TAB 字符包围、=
可选地由 SPC/TAB 字符或一个或多个 SPC/TAB 字符的序列包围)并且位于末尾时,这才会出现问题。\!
它视为以或开头的注释行\#
。仅对于名称以!
或开头的属性有问题#
。在
prop=1\ 2\ 3
我们得到的1 2 3
是123
: 连续行中的前导空格不会被忽略,因为它们应该如此。
²IFS 空白字符[:space:]
,每个 POSIX 是在语言环境中分类的字符(通常包括\f
但不是必须),并且恰好$IFS
在 ksh88(POSIX 规范所基于的)中并且在大多数 shell 中,这仍然限于 SPC 、TAB 和 NL。我发现在这方面唯一符合 POSIX 标准的 shell 是yash
.ksh93
并且bash
(自 5.0 起)还包括其他空格(例如 CR、FF、VT...),但仅限于单字节空格(请注意某些系统(如 Solaris),其中包括单字节的不间断空格在某些地区)
答案2
这是 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
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
所提供的数据样本中该脚本的输出:
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" )
答案3
对于该数据格式最常见的子集,您可以使用一个短函数,使用bash 变量扩展和正则表达式匹配。
注意:这要求行采用^key = value$
格式,或者^#.*$
用于^!.*$
注释。调整代码,或以其他方式预处理数据
$ cat /tmp/propdata
k1 = v1
# A comment
k2 = v2and some s=t=u=f=f
! Another comment
k3 = v3
$ unset DATA
$ declare -A DATA
$ props(){ while read line || [[ -n $line ]]; do
[[ "$line" =~ ^#|^! ]] && continue;
if [[ "${line% =*}" ]]; then DATA[${line% =*}]="${line#*= }" ; fi ;
done < $1 ; }
$ props /tmp/propdata
$ echo "${DATA[k3]}"
v3
$ echo "${DATA[k2]}"
v2and some s=t=u=f=f
编辑:更新以修剪键和值的“=”周围的空格
Edit2:现在也过滤评论。
答案4
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
}
用法:
readPopertyFile "file.properties"
将把属性读入名为 的关联数组变量中properties
。
* 在 bash 中工作。不知道其他的贝壳怎么样。
* 不会处理多行属性。