正在 bash 中解析 .ini 文件
Parsing .ini file in bash
我有一个下面的属性文件,想按如下所述解析它。请帮忙做这件事。
.ini 我创建的文件:
[Machine1]
app=version1
[Machine2]
app=version1
app=version2
[Machine3]
app=version1
app=version3
我正在寻找一个解决方案,其中 ini 文件应该像
一样被解析
[Machine1]app = version1
[Machine2]app = version1
[Machine2]app = version2
[Machine3]app = version1
[Machine3]app = version3
谢谢。
尝试:
$ awk '/\[/{prefix=[=10=]; next} {print prefix [=10=]}' file.ini
[Machine1]app=version1
[Machine2]app=version1
[Machine2]app=version2
[Machine3]app=version1
[Machine3]app=version3
工作原理
/\[/{prefix=[=12=]; next}
如果任何行以 [
开头,我们将该行保存在变量 prefix
中,然后跳过其余命令并跳转到 next
行。
{print prefix [=16=]}
如果当前行不为空,我们打印当前行后面的前缀。
添加空格
在出现的任何 =
周围添加空格:
$ awk -F= '/\[/{prefix=[=11=]; next} {=; print prefix [=11=]}' OFS=' = ' file.ini
[Machine1]app = version1
[Machine2]app = version1
[Machine2]app = version2
[Machine3]app = version1
[Machine3]app = version3
这通过使用 =
作为输入的字段分隔符和 =
作为输出的字段分隔符来实现。
您可以尝试使用 awk
:
awk '/\[[^]]*\]/{ # Match pattern like [...]
a=;next # store the pattern in a
}
NF{ # Match non empty line
gsub("=", " = ") # Add space around the = character
print a [=10=] # print the line
}' file
我喜欢@John1024 的回答。我正在寻找那个。我创建了一个 bash 函数,允许我根据他的想法查找部分或特定键:
function iniget() {
if [[ $# -lt 2 || ! -f ]]; then
echo "usage: iniget <file> [--list|<section> [key]]"
return 1
fi
local inifile=
if [ "" == "--list" ]; then
for section in $(cat $inifile | grep "\[" | sed -e "s#\[##g" | sed -e "s#\]##g"); do
echo $section
done
return 0
fi
local section=
local key
[ $# -eq 3 ] && key=
#
# This awk line turns ini sections => [section-name]key=value
local lines=$(awk '/\[/{prefix=[=10=]; next} {print prefix [=10=]}' $inifile)
for line in $lines; do
if [[ "$line" = \[$section\]* ]]; then
local keyval=$(echo $line | sed -e "s/^\[$section\]//")
if [[ -z "$key" ]]; then
echo $keyval
else
if [[ "$keyval" = $key=* ]]; then
echo $(echo $keyval | sed -e "s/^$key=//")
fi
fi
fi
done
}
所以给出这个 file.ini
[Machine1]
app=version1
[Machine2]
app=version1
app=version2
[Machine3]
app=version1
app=version3
则产生如下结果
$ iniget file.ini --list
Machine1
Machine2
Machine3
$ iniget file.ini Machine3
app=version1
app=version3
$ iniget file.ini Machine1 app
version1
$ iniget file.ini Machine2 app
version2
version3
再次感谢@John1024 的回答,我竭尽全力试图创建一个简单的 bash 支持部分的 ini 解析器。
Tested on Mac using GNU bash, version 5.0.0(1)-release (x86_64-apple-darwin18.2.0)
这里有很好的答案。我对@davfive 的功能进行了一些修改,以使其更适合我的用例。这个版本在很大程度上是相同的,除了它允许在 =
个字符前后有空格,并允许值中有空格。
# Get values from a .ini file
function iniget() {
if [[ $# -lt 2 || ! -f ]]; then
echo "usage: iniget <file> [--list|<section> [key]]"
return 1
fi
local inifile=
if [ "" == "--list" ]; then
for section in $(cat $inifile | grep "^\s*\[" | sed -e "s#\[##g" | sed -e "s#\]##g"); do
echo $section
done
return 0
fi
local section=
local key
[ $# -eq 3 ] && key=
# This awk line turns ini sections => [section-name]key=value
local lines=$(awk '/\[/{prefix=[=10=]; next} {print prefix [=10=]}' $inifile)
lines=$(echo "$lines" | sed -e 's/[[:blank:]]*=[[:blank:]]*/=/g')
while read -r line ; do
if [[ "$line" = \[$section\]* ]]; then
local keyval=$(echo "$line" | sed -e "s/^\[$section\]//")
if [[ -z "$key" ]]; then
echo $keyval
else
if [[ "$keyval" = $key=* ]]; then
echo $(echo $keyval | sed -e "s/^$key=//")
fi
fi
fi
done <<<"$lines"
}
为了采用不同的部分并将部分名称(包括 'no-section'/Default 一起)添加到其每个相关关键字(连同 =
及其键值),此 one-liner AWK 可以结合一些 clean-up 正则表达式来解决问题。
ini_buffer="$(echo "$raw_buffer" | awk '/^\[.*\]$/{obj=[=10=]}/=/{print obj [=10=]}')"
将按照您的要求输出台词:
+++ awk '/^\[.*\]$/{obj=[=11=]}/=/{print obj [=11=]}'
++ ini_buffer='[Machine1]app=version1
[Machine2]app=version1
[Machine2]app=version2
[Machine3]app=version1
[Machine3]app=version3'
INI-format 文件的完整解决方案
正如Clonato, INI-format expert所说,对于最新的 INI 版本 1.4 (2009-10-23),INI 文件还有其他几个棘手的方面:
- 节名称的字符集约束
- 关键字的字符集约束
- 最后是键值能够处理大部分未在部分和关键字名称中使用的内容;这包括在一对相同的 single/double-quote.
中嵌套引号
除了引号的嵌套,一个INI-format Github解析INI-format文件默认部分的完整解决方案:
# syntax: ini_file_read <raw_buffer>
# outputs: formatted bracket-nested "[section]keyword=keyvalue"
ini_file_read()
{
local ini_buffer raw_buffer hidden_default
raw_buffer=""
# somebody has to remove the 'inline' comment
# there is a most complex SED solution to nested
# quotes inline comment coming ... TBA
raw_buffer="$(echo "$raw_buffer" | sed '
s|[[:blank:]]*//.*||; # remove //comments
s|[[:blank:]]*#.*||; # remove #comments
t prune
b
:prune
/./!d; # remove empty lines, but only those that
# become empty as a result of comment stripping'
)"
# awk does the removal of leading and trailing spaces
ini_buffer="$(echo "$raw_buffer" | awk '/^\[.*\]$/{obj=[=12=]}/=/{print obj [=12=]}')" # original
ini_buffer="$(echo "$ini_buffer" | sed 's/^\s*\[\s*/\[/')"
ini_buffer="$(echo "$ini_buffer" | sed 's/\s*\]\s*/\]/')"
# finds all 'no-section' and inserts '[Default]'
hidden_default="$(echo "$ini_buffer" \
| egrep '^[-0-9A-Za-z_$\.]+=' | sed 's/^/[Default]/')"
if [ -n "$hidden_default" ]; then
echo "$hidden_default"
fi
# finds sectional and outputs as-is
echo "$(echo "$ini_buffer" | egrep '^\[\s*[-0-9A-Za-z_$\.]+\s*\]')"
}
此 Whosebug post 的单元测试包含在此文件中:
来源:
我有一个下面的属性文件,想按如下所述解析它。请帮忙做这件事。
.ini 我创建的文件:
[Machine1]
app=version1
[Machine2]
app=version1
app=version2
[Machine3]
app=version1
app=version3
我正在寻找一个解决方案,其中 ini 文件应该像
一样被解析[Machine1]app = version1
[Machine2]app = version1
[Machine2]app = version2
[Machine3]app = version1
[Machine3]app = version3
谢谢。
尝试:
$ awk '/\[/{prefix=[=10=]; next} {print prefix [=10=]}' file.ini
[Machine1]app=version1
[Machine2]app=version1
[Machine2]app=version2
[Machine3]app=version1
[Machine3]app=version3
工作原理
/\[/{prefix=[=12=]; next}
如果任何行以
[
开头,我们将该行保存在变量prefix
中,然后跳过其余命令并跳转到next
行。{print prefix [=16=]}
如果当前行不为空,我们打印当前行后面的前缀。
添加空格
在出现的任何 =
周围添加空格:
$ awk -F= '/\[/{prefix=[=11=]; next} {=; print prefix [=11=]}' OFS=' = ' file.ini
[Machine1]app = version1
[Machine2]app = version1
[Machine2]app = version2
[Machine3]app = version1
[Machine3]app = version3
这通过使用 =
作为输入的字段分隔符和 =
作为输出的字段分隔符来实现。
您可以尝试使用 awk
:
awk '/\[[^]]*\]/{ # Match pattern like [...]
a=;next # store the pattern in a
}
NF{ # Match non empty line
gsub("=", " = ") # Add space around the = character
print a [=10=] # print the line
}' file
我喜欢@John1024 的回答。我正在寻找那个。我创建了一个 bash 函数,允许我根据他的想法查找部分或特定键:
function iniget() {
if [[ $# -lt 2 || ! -f ]]; then
echo "usage: iniget <file> [--list|<section> [key]]"
return 1
fi
local inifile=
if [ "" == "--list" ]; then
for section in $(cat $inifile | grep "\[" | sed -e "s#\[##g" | sed -e "s#\]##g"); do
echo $section
done
return 0
fi
local section=
local key
[ $# -eq 3 ] && key=
#
# This awk line turns ini sections => [section-name]key=value
local lines=$(awk '/\[/{prefix=[=10=]; next} {print prefix [=10=]}' $inifile)
for line in $lines; do
if [[ "$line" = \[$section\]* ]]; then
local keyval=$(echo $line | sed -e "s/^\[$section\]//")
if [[ -z "$key" ]]; then
echo $keyval
else
if [[ "$keyval" = $key=* ]]; then
echo $(echo $keyval | sed -e "s/^$key=//")
fi
fi
fi
done
}
所以给出这个 file.ini
[Machine1]
app=version1
[Machine2]
app=version1
app=version2
[Machine3]
app=version1
app=version3
则产生如下结果
$ iniget file.ini --list
Machine1
Machine2
Machine3
$ iniget file.ini Machine3
app=version1
app=version3
$ iniget file.ini Machine1 app
version1
$ iniget file.ini Machine2 app
version2
version3
再次感谢@John1024 的回答,我竭尽全力试图创建一个简单的 bash 支持部分的 ini 解析器。
Tested on Mac using GNU bash, version 5.0.0(1)-release (x86_64-apple-darwin18.2.0)
这里有很好的答案。我对@davfive 的功能进行了一些修改,以使其更适合我的用例。这个版本在很大程度上是相同的,除了它允许在 =
个字符前后有空格,并允许值中有空格。
# Get values from a .ini file
function iniget() {
if [[ $# -lt 2 || ! -f ]]; then
echo "usage: iniget <file> [--list|<section> [key]]"
return 1
fi
local inifile=
if [ "" == "--list" ]; then
for section in $(cat $inifile | grep "^\s*\[" | sed -e "s#\[##g" | sed -e "s#\]##g"); do
echo $section
done
return 0
fi
local section=
local key
[ $# -eq 3 ] && key=
# This awk line turns ini sections => [section-name]key=value
local lines=$(awk '/\[/{prefix=[=10=]; next} {print prefix [=10=]}' $inifile)
lines=$(echo "$lines" | sed -e 's/[[:blank:]]*=[[:blank:]]*/=/g')
while read -r line ; do
if [[ "$line" = \[$section\]* ]]; then
local keyval=$(echo "$line" | sed -e "s/^\[$section\]//")
if [[ -z "$key" ]]; then
echo $keyval
else
if [[ "$keyval" = $key=* ]]; then
echo $(echo $keyval | sed -e "s/^$key=//")
fi
fi
fi
done <<<"$lines"
}
为了采用不同的部分并将部分名称(包括 'no-section'/Default 一起)添加到其每个相关关键字(连同 =
及其键值),此 one-liner AWK 可以结合一些 clean-up 正则表达式来解决问题。
ini_buffer="$(echo "$raw_buffer" | awk '/^\[.*\]$/{obj=[=10=]}/=/{print obj [=10=]}')"
将按照您的要求输出台词:
+++ awk '/^\[.*\]$/{obj=[=11=]}/=/{print obj [=11=]}'
++ ini_buffer='[Machine1]app=version1
[Machine2]app=version1
[Machine2]app=version2
[Machine3]app=version1
[Machine3]app=version3'
INI-format 文件的完整解决方案
正如Clonato, INI-format expert所说,对于最新的 INI 版本 1.4 (2009-10-23),INI 文件还有其他几个棘手的方面:
- 节名称的字符集约束
- 关键字的字符集约束
- 最后是键值能够处理大部分未在部分和关键字名称中使用的内容;这包括在一对相同的 single/double-quote. 中嵌套引号
除了引号的嵌套,一个INI-format Github解析INI-format文件默认部分的完整解决方案:
# syntax: ini_file_read <raw_buffer>
# outputs: formatted bracket-nested "[section]keyword=keyvalue"
ini_file_read()
{
local ini_buffer raw_buffer hidden_default
raw_buffer=""
# somebody has to remove the 'inline' comment
# there is a most complex SED solution to nested
# quotes inline comment coming ... TBA
raw_buffer="$(echo "$raw_buffer" | sed '
s|[[:blank:]]*//.*||; # remove //comments
s|[[:blank:]]*#.*||; # remove #comments
t prune
b
:prune
/./!d; # remove empty lines, but only those that
# become empty as a result of comment stripping'
)"
# awk does the removal of leading and trailing spaces
ini_buffer="$(echo "$raw_buffer" | awk '/^\[.*\]$/{obj=[=12=]}/=/{print obj [=12=]}')" # original
ini_buffer="$(echo "$ini_buffer" | sed 's/^\s*\[\s*/\[/')"
ini_buffer="$(echo "$ini_buffer" | sed 's/\s*\]\s*/\]/')"
# finds all 'no-section' and inserts '[Default]'
hidden_default="$(echo "$ini_buffer" \
| egrep '^[-0-9A-Za-z_$\.]+=' | sed 's/^/[Default]/')"
if [ -n "$hidden_default" ]; then
echo "$hidden_default"
fi
# finds sectional and outputs as-is
echo "$(echo "$ini_buffer" | egrep '^\[\s*[-0-9A-Za-z_$\.]+\s*\]')"
}
此 Whosebug post 的单元测试包含在此文件中:
来源: