如何将属性文件读入关联数组?

如何将属性文件读入关联数组?

我想读取 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=\ barpassword_with\\backslash-and=equals
  • 带反斜杠的续行

但是,如果我们检查规格

  • 这不处理\n, \r, \uXXXX... 序列

  • LF 是唯一可识别的行分隔符(不是 CR 也不是 CRLF)。

  • FF 不被识别为空白(我们不能只是将其添加到$IFSas ,具体取决于 shell 和版本,\f不一定会被识别为 IFS 空白字符 1)。

  • 对于像 之类的输入foo: bar =,存储barin${props[foo]}而不是bar =(foo: bar:baz:不过也可以)。仅当属性值包含一个(未转义的)分隔符(:可选地由 SPC/TAB 字符包围、=可选地由 SPC/TAB 字符或一个或多个 SPC/TAB 字符的序列包围)并且位于末尾时,这才会出现问题。

  • \!它视为以或开头的注释行\#。仅对于名称以!或开头的属性有问题#

  •   prop=1\
       2\
       3
    

我们得到的1 2 3123: 连续行中的前导空格不会被忽略,因为它们应该如此。


²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 中工作。不知道其他的贝壳怎么样。

* 不会处理多行属性。

相关内容