如何解析其值可能包含某些字符的 ini 文件?

如何解析其值可能包含某些字符的 ini 文件?

我看过几个 bash ini 解析脚本,我看到了一个在这里用过几次,所以我想看看它是否适合我。看起来它会多次逐行读取 ini 文件,并且每次传递都会逐步构造一个最终被评估的函数。它适用于某些特殊字符,但不适用于其他字符。如果文件中的值包含单引号或大于/小于符号,则脚本返回语法错误。其他符号也会产生意想不到的结果。遇到这些字符时我该如何处理?

这是解析ini的函数。

#!/usr/bin/env bash
cfg_parser ()
{
    ini="$(<$1)"                # read the file
    ini="${ini//[/\[}"          # escape [
    ini="${ini//]/\]}"          # escape ]
    IFS=$'\n' && ini=( ${ini} ) # convert to line-array
    ini=( ${ini[*]//;*/} )      # remove comments with ;
    ini=( ${ini[*]/\    =/=} )  # remove tabs before =
    ini=( ${ini[*]/=\   /=} )   # remove tabs be =
    ini=( ${ini[*]/\ =\ /=} )   # remove anything with a space around =
    ini=( ${ini[*]/#\\[/\}$'\n'cfg.section.} ) # set section prefix
    ini=( ${ini[*]/%\\]/ \(} )    # convert text2function (1)
    ini=( ${ini[*]/=/=\( } )    # convert item to array
    ini=( ${ini[*]/%/ \)} )     # close array parenthesis
    ini=( ${ini[*]/%\\ \)/ \\} ) # the multiline trick
    ini=( ${ini[*]/%\( \)/\(\) \{} ) # convert text2function (2)
    ini=( ${ini[*]/%\} \)/\}} ) # remove extra parenthesis
    ini[0]="" # remove first element
    ini[${#ini[*]} + 1]='}'    # add the last brace
    eval "$(echo "${ini[*]}")" # eval the result
}

ini文件

[Section1]
value1=abc`def # unexpected EOF while looking for matching ``'
value2=ghi>jkl # syntax error near unexpected token `>'
value3=mno$pqr # executes ok but outputs "mnoqr"
value4=stu;vwx # executes ok but outputs "stu"

答案1

事实是你做某事bash并不意味着你应该

shbash等)脚本最适合作为相对简单的包装器来启动程序或围绕文本处理命令。对于更复杂的任务,包括解析 ini 文件并对其进行操作,其他语言更合适。您是否考虑过用perlor编写脚本python?两者都有很好的 .ini 文件解析器 -Config::INI当我需要解析 ini 文件时,我曾多次使用过 perl 的模块。

但如果您坚持在 bash 中执行此操作,则应该使用关联数组而不是设置单个变量。

从这样的事情开始:

#! /bin/bash

inifile='user1074170.ini' 

# declare $config to be an associative array
declare -A config

while IFS='=' read -r key val ; do 
    config["$key"]="$val"
done <  <(sed -E -e '/^\[/d
                     s/#.*//
                     s/[[:blank:]]+$|^[[:blank:]]+//g' "$inifile" )

# now print out the config array
set | grep '^config='

sed脚本删除该[Section1]行(实际上,所有以开方括号开头的行[- 您将需要在具有多个部分的 ini 文件中以不同方式处理此问题[1]),并删除注释以及前导和尾随空格。该while循环读取每一行,用作=字段分隔符,并将内容分配给变量 $key 和 $val,然后将其添加到 $config 数组中。

输出:

config=([value1]="abc\`def" [value3]="mno\$pqr" [value2]="ghi>jkl" [value4]="stu;vwx" )

您可以稍后在脚本中使用数组条目,如下所示:

$ echo value1 is "${config[value1]}"
value1 is abc`def

$ [ "${config[value4]}" = 'stu;vwx' ] && echo true
true

[1]awk或者perl有以“段落”模式方便地阅读文件的简单方法。段落被定义为由一个或多个空行与其他文本块分隔开的文本块。

例如,要仅使用[Section1],请在将脚本输入到上面的循环awk之前插入下面的脚本:sedwhile

awk -v RS= -v ORS='\n\n' '/\[Section1\]/' "$inifile" | sed ...

"$inifile"(当然,并从命令行末尾删除sed- 在您费尽心思从中提取文件之后,您不想再次输入该文件[Section1])。

如果您只从 ini 文件中提取一个部分,则设置ORS并不是绝对必要的 - 但如果您要提取两个或多个部分,则对于保持段落分隔很有用。

答案2

我知道这是一个不完整的答案,但MySQL.lnsin augeas 似乎能够解析其中的大部分内容。在augtool

augtool> set /augeas/load/testini/incl "/root/test.ini"
augtool> set /augeas/load/testini/lens "MySQL.lns"
augtool> load
augtool> ls /files/root/
.ssh/      test.ini/
augtool> ls /files/root/test.ini
target/ = Section1
augtool> ls /files/root/test.ini/target/
value1/ = abc`def
value2/ = ghi>jkl
value3/ = mno$pqr
value4/ = stu

唯一搞砸的是最后一个,说实话,我不认为这是一个错误。在.ini文件中,分号标记注释的开始。我还想问一下你的数据实际上是这样的吗?

如果是这样,您可以sed在它之前做一些设置,设置;一些未使用的字符值,然后将其转换回后处理。但最终,您将需要一些标准,以便文件能够具有任何可辨别的结构。

编辑:

我用 PHP 镜头对其进行了测试,只要引用这些值就可以得到整个结果:

[root@vlzoreman ~]# augtool
augtool> set /augeas/load/testini/lens "PHP.lns"
augtool> set /augeas/load/testini/incl "/root/test.ini"
augtool> load
augtool>  ls /files/root/test.ini/Section1/
value1 = abc`def
value2 = ghi>jkl
value3 = mno$pqr
value4 = stu;vwx

否则它就和 MySQL 镜头一样远了。

编辑#2:

我确信有一种更简洁的方法可以编写此内容,但这是示例用法:

[root@vlp-foreman ~]# bash bash.sh
Values for: Section1:
        :: value1 is abc`def
        :: value2 is ghi>jkl
        :: value3 is mno$pqr
        :: value4 is stu;vwx
Values for: Section2:
        :: value1 is abc`def

脚本是:

#!/bin/bash

sections=$(augtool -A --transform "PHP.lns incl /root/test.ini" ls /files/root/test.ini | cut -f1 -d/)

for currentSection in $sections; do

  echo "Values for: $currentSection:"

  fields=$(augtool -A --transform "PHP.lns incl /root/test.ini" ls /files/root/test.ini/$currentSection | awk '{print $1}')

  for currentField in $fields; do

    currentValue=$(augtool -A --transform "PHP.lns incl /root/test.ini" print /files/root/test.ini/$currentSection/$currentField | cut -f2 -d=)
    currentValue=$(echo $currentValue | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//' | sed -e 's/^"//' -e 's/"$//')

    echo -e "\t:: $currentField is $currentValue"

  done

done

答案3

看一眼crudinihttps://www.pixelbeat.org/programs/crudini/
在 Ubuntu 上,您可以安装它以sudo apt install crudini
从 ini 文件中读取值,运行:

$ value1=$(crudini --get "crudini.ini" "Section1" "value1")

crudini 支持多种 ini 文件格式并处理特殊字符:

克鲁迪尼.ini

[Section1]
value1=abc`def
value2=ghi>jkl
value3=mno$pqr
value4=stu;vwx

读数值

$ for i in {1..4}; do crudini --get "crudini.ini" "Section1" "value$i"; done
abc`def
ghi>jkl
mno$pqr
stu;vwx
$

相关内容