我看过几个 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
并不意味着你应该。
sh
(bash
等)脚本最适合作为相对简单的包装器来启动程序或围绕文本处理命令。对于更复杂的任务,包括解析 ini 文件并对其进行操作,其他语言更合适。您是否考虑过用perl
or编写脚本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
之前插入下面的脚本:sed
while
awk -v RS= -v ORS='\n\n' '/\[Section1\]/' "$inifile" | sed ...
"$inifile"
(当然,并从命令行末尾删除sed
- 在您费尽心思从中提取文件之后,您不想再次输入该文件[Section1]
)。
如果您只从 ini 文件中提取一个部分,则设置ORS
并不是绝对必要的 - 但如果您要提取两个或多个部分,则对于保持段落分隔很有用。
答案2
我知道这是一个不完整的答案,但MySQL.lns
in 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
看一眼crudini
:https://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
$