如何在 shell 脚本中获取 INI 值?

我有一个 parters.ini 文件,比如:

[parameters.ini]
database_user    = user
database_version = 20110611142248

我希望从 bash shell 脚本中读入并使用 Parameter ters.ini 文件中指定的数据库版本,这样我就可以处理它了。

#!/bin/sh
# Need to get database version from parameters.ini file to use in script
php app/console doctrine:migrations:migrate $DATABASE_VERSION

我要怎么做?

136880 次浏览

如何使用 awk 来获取那条线

version=$(awk -F "=" '/database_version/ {print $2}' parameters.ini)

Bash 不为这些文件提供解析器。显然,您可以使用 awk 命令或几个 sed 调用,但是如果您是 bash-祭司并且不想使用任何其他 shell,那么您可以尝试以下晦涩的代码:

#!/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 after =
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
}


cfg_writer ()
{
IFS=' '$'\n'
fun="$(declare -F)"
fun="${fun//declare -f/}"
for f in $fun; do
[ "${f#cfg.section}" == "${f}" ] && continue
item="$(declare -f ${f})"
item="${item##*\{}"
item="${item%\}}"
item="${item//=*;/}"
vars="${item//=*/}"
eval $f
echo "[${f#cfg.section.}]"
for var in $vars; do
echo $var=\"${!var}\"
done
done
}

用法:

# parse the config file called 'myfile.ini', with the following
# contents::
#   [sec2]
#   var2='something'
cfg.parser 'myfile.ini'


# enable section called 'sec2' (in the file [sec2]) for reading
cfg.section.sec2


# read the content of the variable called 'var2' (in the file
# var2=XXX). If your var2 is an array, then you can use
# ${var[index]}
echo "$var2"

可以在 The Old School DevOps 博客网站找到 Bash ini 解析器。

一个更可能的解决办法

dbver=$(sed -n 's/.*database_version *= *\([^ ]*.*\)/\1/p' < parameters.ini)
echo $dbver

到目前为止,我所看到的所有解决方案都符合注释掉的行。如果注释代码是 ;,这个没有:

awk -F '=' '{if (! ($0 ~ /^;/) && $0 ~ /database_version/) print $2}' file.ini

这个脚本将获得如下参数:

意思是如果你的 ini 有:

Pars _ ini.Ksh < path to Ini file > < name of Section in Ini file > < the name in name = value to return >

怎么称呼它:


[环境]

A = x

[数据库 _ 扇区]

DSN = 东西


然后打电话给:

Pars _ ini. ksh/users/Bubu _ user/Parameter ters.ini 数据库扇区 DSN

这将检索以下“某物”

脚本“ pars _ ini. ksh”:

\#!/bin/ksh


\#INI_FILE=path/to/file.ini


\#INI_SECTION=TheSection


\# BEGIN parse-ini-file.sh


\# SET UP THE MINIMUM VARS FIRST


alias sed=/usr/local/bin/sed


INI_FILE=$1


INI_SECTION=$2


INI_NAME=$3


INI_VALUE=""




eval `sed -e 's/[[:space:]]*\=[[:space:]]*/=/g' \


-e 's/;.*$//' \


-e 's/[[:space:]]*$//' \


-e 's/^[[:space:]]*//' \


-e "s/^\(.*\)=\([^\"']*\)$/\1=\"\2\"/" \


< $INI_FILE  \


| sed -n -e "/^\[$INI_SECTION\]/,/^\s*\[/{/^[^;].*\=.*/p;}"`




TEMP_VALUE=`echo "$"$INI_NAME`


echo `eval echo $TEMP_VALUE`

您可以使用 crudini工具获取 ini 值,例如:

DATABASE_VERSION=$(crudini --get parameters.ini '' database_version)

在 ini 样式的 我的档案中显示 我的钥匙的值:

sed -n -e 's/^\s*my_key\s*=\s*//p' my_file
  • -n——默认情况下不打印任何内容
  • -e——执行表达式
  • s/PATTERN//p——显示符合此模式的任何内容 模式如下:
  • ^——模式从行的开头开始
  • 空白字符
  • *——零或多个(空白字符)

例如:

$ cat my_file
# Example INI file
something   = foo
my_key      = bar
not_my_key  = baz
my_key_2    = bing


$ sed -n -e 's/^\s*my_key\s*=\s*//p' my_file
bar

所以:

找到一个以零或多个空格字符开头的模式, 后面跟着字符串 我的钥匙,后面跟着零个或多个空格字符,一个等号,然后再次跟着零个或多个空格字符。按照该模式在该行上显示其余内容。

您可以使用 bash 本机解析器通过以下方式解释 ini 值:

$ source <(grep = file.ini)

示例文件:

[section-a]
var1=value1
var2=value2
IPS=( "1.2.3.4" "1.2.3.5" )

要访问变量,您只需打印它们: echo $var1。您也可以使用数组,如上所示(echo ${IPS[@]})。

如果只需要一个值,那么只需要 grep:

source <(grep var1 file.ini)

为了演示,在电影院检查这个录音

它很简单,因为您不需要任何外部库来解析数据,但是它也有一些缺点。例如:

  • 如果在 =(变量名和值)之间有空格,那么必须首先修剪这些空格,例如。

      $ source <(grep = file.ini | sed 's/ *= */=/g')
    

    或者如果你不在乎空格(包括中间的空格) ,使用:

      $ source <(grep = file.ini | tr -d ' ')
    
  • 要支持 ;评论,请用 #替换它们:

      $ sed "s/;/#/g" foo.ini | source /dev/stdin
    
  • 这些部分不受支持(例如,如果您有 [section-name],那么您必须像上面所示的那样过滤掉它,例如 grep =) ,对于其他意外错误也是如此。

    如果您需要读取特定部分下的特定值,请使用 grep -Asedawkex)。

    例如。

      source <(grep = <(grep -A5 '\[section-b\]' file.ini))
    

    注意: 其中 -A5是本节中要读取的行数。将 source替换为 cat以进行调试。

  • 如果有任何解析错误,可以通过添加: 2>/dev/null忽略它们

参见:

只需将.ini 文件包含到 bash 主体中:

文件 Example.ini:

DBNAME=test
DBUSER=scott
DBPASSWORD=tiger

文件 Example.sh

#!/bin/bash
#Including .ini file
. example.ini
#Test
echo "${DBNAME}   ${DBUSER}  ${DBPASSWORD}"

我的俏皮话版本

#!/bin/bash
#Reader for MS Windows 3.1 Ini-files
#Usage: inireader.sh


# e.g.: inireader.sh win.ini ERRORS DISABLE
# would return value "no" from the section of win.ini
#[ERRORS]
#DISABLE=no
INIFILE=$1
SECTION=$2
ITEM=$3
cat $INIFILE | sed -n /^\[$SECTION\]/,/^\[.*\]/p | grep "^[:space:]*$ITEM[:space:]*=" | sed s/.*=[:space:]*//

当我在 base64中使用密码时,我放置分隔符“ :”,因为 base64字符串可能有“ =”。例如(我使用 ksh) :

> echo "Abc123" | base64
QWJjMTIzCg==

parameters.ini中加入线 pass:QWJjMTIzCg==,最后:

> PASS=`awk -F":" '/pass/ {print $2 }' parameters.ini | base64 --decode`
> echo "$PASS"
Abc123

如果这条线有像 "pass : QWJjMTIzCg== "这样的空格,那么添加 | tr -d ' '来修剪它们:

> PASS=`awk -F":" '/pass/ {print $2 }' parameters.ini | tr -d ' ' | base64 --decode`
> echo "[$PASS]"
[Abc123]

刚写完我自己的解析器。我尝试使用这里发现的各种解析器,似乎没有一个能同时使用 ksh93(AIX)和 bash (Linux)。

这是老式的编程风格——逐行解析。很快,因为它只用了很少的外部命令。因为数组的动态名称需要所有的 eval,所以稍微慢一点。

Ini 支持3种特殊的语法:

  • Include/defile = ini file —— > 加载一个附加的 ini 文件。用于将 ini 分成多个文件,或者重用某个配置
  • 目录—— > 与 include 文件相同,但包含一个完整的目录
  • Include essection = section —— > 将现有节复制到当前节。

我使用所有这些语法来得到相当复杂的、可重用的 ini 文件。在安装新操作系统时安装产品很有用——我们经常这样做。

可以使用 ${ ini [ $section. $item ]}访问值。

玩得开心,希望对别人有用!

function Show_Debug {
[[ $DEBUG = YES ]] && echo "DEBUG $@"
}


function Fatal {
echo "$@. Script aborted"
exit 2
}
#-------------------------------------------------------------------------------
# This function load an ini file in the array "ini"
# The "ini" array must be defined in the calling program (typeset -A ini)
#
# It could be any array name, the default array name is "ini".
#
# There is heavy usage of "eval" since ksh and bash do not support
# reference variable. The name of the ini is passed as variable, and must
# be "eval" at run-time to work. Very specific syntax was used and must be
# understood before making any modifications.
#
# It complexify greatly the program, but add flexibility.
#-------------------------------------------------------------------------------


function Load_Ini {
Show_Debug "$0($@)"
typeset ini_file="$1"
# Name of the array to fill. By default, it's "ini"
typeset ini_array_name="${2:-ini}"
typeset section variable value line my_section file subsection value_array include_directory all_index index sections pre_parse
typeset LF="
"
if [[ ! -s $ini_file ]]; then
Fatal "The ini file is empty or absent in $0 [$ini_file]"
fi


include_directory=$(dirname $ini_file)
include_directory=${include_directory:-$(pwd)}


Show_Debug "include_directory=$include_directory"


section=""
# Since this code support both bash and ksh93, you cannot use
# the syntax "echo xyz|while read line". bash doesn't work like
# that.
# It forces the use of "<<<", introduced in bash and ksh93.


Show_Debug "Reading file $ini_file and putting the results in array $ini_array_name"
pre_parse="$(sed 's/^ *//g;s/#.*//g;s/ *$//g' <$ini_file | egrep -v '^$')"
while read line; do
if [[ ${line:0:1} = "[" ]]; then # Is the line starting with "["?
# Replace [section_name] to section_name by removing the first and last character
section="${line:1}"
section="${section%\]}"
eval "sections=\${$ini_array_name[sections_list]}"
sections="$sections${sections:+ }$section"
eval "$ini_array_name[sections_list]=\"$sections\""
Show_Debug "$ini_array_name[sections_list]=\"$sections\""
eval "$ini_array_name[$section.exist]=YES"
Show_Debug "$ini_array_name[$section.exist]='YES'"
else
variable=${line%%=*}   # content before the =
value=${line#*=}       # content after the =


if [[ $variable = includefile ]]; then
# Include a single file
Load_Ini "$include_directory/$value" "$ini_array_name"
continue
elif [[ $variable = includedir ]]; then
# Include a directory
# If the value doesn't start with a /, add the calculated include_directory
if [[ $value != /* ]]; then
value="$include_directory/$value"
fi
# go thru each file
for file in $(ls $value/*.ini 2>/dev/null); do
if [[ $file != *.ini ]]; then continue; fi
# Load a single file
Load_Ini "$file" "$ini_array_name"
done
continue
elif [[ $variable = includesection ]]; then
# Copy an existing section into the current section
eval "all_index=\"\${!$ini_array_name[@]}\""
# It's not necessarily fast. Need to go thru all the array
for index in $all_index; do
# Only if it is the requested section
if [[ $index = $value.* ]]; then
# Evaluate the subsection [section.subsection] --> subsection
subsection=${index#*.}
# Get the current value (source section)
eval "value_array=\"\${$ini_array_name[$index]}\""
# Assign the value to the current section
# The $value_array must be resolved on the second pass of the eval, so make sure the
# first pass doesn't resolve it (\$value_array instead of $value_array).
# It must be evaluated on the second pass in case there is special character like $1,
# or ' or " in it (code).
eval "$ini_array_name[$section.$subsection]=\"\$value_array\""
Show_Debug "$ini_array_name[$section.$subsection]=\"$value_array\""
fi
done
fi


# Add the value to the array
eval "current_value=\"\${$ini_array_name[$section.$variable]}\""
# If there's already something for this field, add it with the current
# content separated by a LF (line_feed)
new_value="$current_value${current_value:+$LF}$value"
# Assign the content
# The $new_value must be resolved on the second pass of the eval, so make sure the
# first pass doesn't resolve it (\$new_value instead of $new_value).
# It must be evaluated on the second pass in case there is special character like $1,
# or ' or " in it (code).
eval "$ini_array_name[$section.$variable]=\"\$new_value\""
Show_Debug "$ini_array_name[$section.$variable]=\"$new_value\""
fi
done  <<< "$pre_parse"
Show_Debug "exit $0($@)\n"
}

这种实现使用 awk,具有以下优点:

  1. 将只返回第一个匹配项
  2. 忽略以 ;开头的行
  3. 修剪前导空格和尾随空格,但不修剪内部空格

格式化版本 :

awk -F '=' '/^\s*database_version\s*=/ {
sub(/^ +/, "", $2);
sub(/ +$/, "", $2);
print $2;
exit;
}' parameters.ini

一行程序 :

awk -F '=' '/^\s*database_version\s*=/ { sub(/^ +/, "", $2); sub(/ +$/, "", $2); print $2; exit; }' parameters.ini

Sed

您可以使用 sed来解析 ini 配置文件,特别是当您有如下部分名称时:

# last modified 1 April 2001 by John Doe
[owner]
name=John Doe
organization=Acme Widgets Inc.


[database]
# use IP address in case network name resolution is not working
server=192.0.2.62
port=143
file=payroll.dat

因此,您可以使用以下 sed脚本来解析上述数据:

# Configuration bindings found outside any section are given to
# to the default section.
1 {
x
s/^/default/
x
}


# Lines starting with a #-character are comments.
/#/n


# Sections are unpacked and stored in the hold space.
/\[/ {
s/\[\(.*\)\]/\1/
x
b
}


# Bindings are unpacked and decorated with the section
# they belong to, before being printed.
/=/ {
s/^[[:space:]]*//
s/[[:space:]]*=[[:space:]]*/|/
G
s/\(.*\)\n\(.*\)/\2|\1/
p
}

这将把 ini 数据转换成这种平面格式:

owner|name|John Doe
owner|organization|Acme Widgets Inc.
database|server|192.0.2.62
database|port|143
database|file|payroll.dat

因此,使用 sedawkread在每一行中使用节名将更容易解析。

来源: Shell 脚本的配置文件,Michael Grünewald


或者,您可以使用这个项目: chilladx/config-parser,一个使用 sed的配置解析器。

我在 bash 脚本中编写了一个快速而简单的 Python 脚本。

例如,您的 ini 文件名为 food.ini 在文件中你可以有一些部分和一些行:

[FRUIT]
Oranges = 14
Apples = 6

复制这个小的6行 Python 脚本并将其保存为 configparser.py

#!/usr/bin/python
import configparser
import sys
config = configparser.ConfigParser()
config.read(sys.argv[1])
print config.get(sys.argv[2],sys.argv[3])

现在,在 bash 脚本中,您可以这样做。

OrangeQty=$(python configparser.py food.ini FRUIT Oranges)

或者

ApplesQty=$(python configparser.py food.ini FRUIT Apples)
echo $ApplesQty

这里的前提是:

  1. 你安装了 Python
  2. 您已经安装了配置解析器库(这应该与 std python 安装一起提供)

希望能有帮助 :¬)

Sed 一行程序,它考虑到各个部分。示例文件:

[section1]
param1=123
param2=345
param3=678


[section2]
param1=abc
param2=def
param3=ghi


[section3]
param1=000
param2=111
param3=222

假设您想要第2部分中的 param2,运行以下命令:

sed -nr "/^\[section2\]/ { :l /^param2[ ]*=/ { s/[^=]*=[ ]*//; p; q;}; n; b l;}" ./file.ini

会给你

def

有些答案不尊重评论。有些人不尊重部门。有些只能识别一种语法(只能识别“ :”或者只能识别“ =”)。由于大写不同或者未能导入 sys 模块,有些 Python 应答在我的机器上失败。对我来说都太简洁了。

所以我写了我自己的,如果你有一个现代的 Python,你可以从你的 Bash shell 调用它。它的优势在于遵循了一些常见的 Python 编码约定,甚至还提供了明显的错误消息和帮助。要使用它,可以将其命名为 myconfig.py (不要称其为 configparser.py,否则它可能会尝试导入自己) ,使其成为可执行的,然后调用它

value=$(myconfig.py something.ini sectionname value)

下面是我在 Linux 上的 Python 3.5代码:

#!/usr/bin/env python3
# Last Modified: Thu Aug  3 13:58:50 PDT 2017
"""A program that Bash can call to parse an .ini file"""


import sys
import configparser
import argparse


if __name__ == '__main__':
parser = argparse.ArgumentParser(description="A program that Bash can call to parse an .ini file")
parser.add_argument("inifile", help="name of the .ini file")
parser.add_argument("section", help="name of the section in the .ini file")
parser.add_argument("itemname", help="name of the desired value")
args = parser.parse_args()


config = configparser.ConfigParser()
config.read(args.inifile)
print(config.get(args.section, args.itemname))

与其他 Python 答案类似,您可以使用 -c标志执行命令行中给出的 Python 语句序列:

$ python3 -c "import configparser; c = configparser.ConfigParser(); c.read('parameters.ini'); print(c['parameters.ini']['database_version'])"
20110611142248

这样做的好处是只需要 Python 标准库,而不需要编写单独的脚本文件。

或者使用 here 文档以获得更好的可读性,比如:

#!/bin/bash
python << EOI
import configparser
c = configparser.ConfigParser()
c.read('params.txt')
print c['chassis']['serialNumber']
EOI


serialNumber=$(python << EOI
import configparser
c = configparser.ConfigParser()
c.read('params.txt')
print c['chassis']['serialNumber']
EOI
)


echo $serialNumber

复杂的简单性

Ini 文件

Test.ini

[section1]
name1=value1
name2=value2
[section2]
name1=value_1
name2  =  value_2

具有读和执行的 bash 脚本

/bin/parseini

#!/bin/bash


set +a
while read p; do
reSec='^\[(.*)\]$'
#reNV='[ ]*([^ ]*)+[ ]*=(.*)'     #Remove only spaces around name
reNV='[ ]*([^ ]*)+[ ]*=[ ]*(.*)'  #Remove spaces around name and spaces before value
if [[ $p =~ $reSec ]]; then
section=${BASH_REMATCH[1]}
elif [[ $p =~ $reNV ]]; then
sNm=${section}_${BASH_REMATCH[1]}
sVa=${BASH_REMATCH[2]}
set -a
eval "$(echo "$sNm"=\""$sVa"\")"
set +a
fi
done < $1

然后在另一个脚本中我来源的命令的结果,并可以使用任何变量内

Test.sh

#!/bin/bash


source parseini test.ini


echo $section2_name2

最后从命令行输出

# ./test.sh
value_2

下面是我的版本,它解析部分并用它填充一个全局关联数组 G _ iniProperties。 注意,这只适用于 Bash v4.2或更高版本。

function parseIniFile() { #accepts the name of the file to parse as argument ($1)
#declare syntax below (-gA) only works with bash 4.2 and higher
unset g_iniProperties
declare -gA g_iniProperties
currentSection=""
while read -r line
do
if [[ $line = [*  ]] ; then
if [[ $line = [* ]] ; then
currentSection=$(echo $line | sed -e 's/\r//g' | tr -d "[]")
fi
else
if [[ $line = *=*  ]] ; then
cleanLine=$(echo $line | sed -e 's/\r//g')
key=$currentSection.$(echo $cleanLine | awk -F: '{ st = index($0,"=");print  substr($0,0,st-1)}')
value=$(echo $cleanLine | awk -F: '{ st = index($0,"=");print  substr($0,st+1)}')
g_iniProperties[$key]=$value
fi
fi;
done < $1
}

下面是使用上面函数的示例代码:

parseIniFile "/path/to/myFile.ini"
for key in "${!g_iniProperties[@]}"; do
echo "Found key/value $key = ${g_iniProperties[$key]}"
done

这使用了系统 perl 和 clean 正则表达式:

cat parameters.ini | perl -0777ne 'print "$1" if /\[\s*parameters\.ini\s*\][\s\S]*?\sdatabase_version\s*=\s*(.*)/'

在其他答案中,“ Karen Gabrielyan”的答案是最好的,但是在一些环境中我们没有问题,比如典型的 busybox,我通过下面的代码更改了答案。

trim()
{
local trimmed="$1"


# Strip leading space.
trimmed="${trimmed## }"
# Strip trailing space.
trimmed="${trimmed%% }"


echo "$trimmed"
}




function parseIniFile() { #accepts the name of the file to parse as argument ($1)
#declare syntax below (-gA) only works with bash 4.2 and higher
unset g_iniProperties
declare -gA g_iniProperties
currentSection=""
while read -r line
do
if [[ $line = [*  ]] ; then
if [[ $line = [* ]] ; then
currentSection=$(echo $line | sed -e 's/\r//g' | tr -d "[]")
fi
else
if [[ $line = *=*  ]] ; then
cleanLine=$(echo $line | sed -e 's/\r//g')
key=$(trim $currentSection.$(echo $cleanLine | cut -d'=' -f1'))
value=$(trim $(echo $cleanLine | cut -d'=' -f2))
g_iniProperties[$key]=$value
fi
fi;
done < $1
}

如果 Python 可用,下面将读取所有节、键和值,并将它们保存在变量中,其名称遵循“[ section ] _ [ key ]”格式。巨蟒可以 x。正确地使用 ini 文件,所以我们使用它。

#!/bin/bash


eval $(python3 << EOP
from configparser import SafeConfigParser


config = SafeConfigParser()
config.read("config.ini"))


for section in config.sections():
for (key, val) in config.items(section):
print(section + "_" + key + "=\"" + val + "\"")
EOP
)


echo "Environment_type:  ${Environment_type}"
echo "Environment_name:  ${Environment_name}"

Config.ini

[Environment]
type                = DEV
name                = D01

可以使用 CSV 解析器 Xsv解析 INI 数据。

cargo install xsv
$ cat /etc/*release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
$ xsv select -d "=" - <<< "$( cat /etc/*release )" | xsv search --no-headers --select 1 "DISTRIB_CODENAME" | xsv select 2
xenial

或者从文件里。

$ xsv select -d "=" - file.ini | xsv search --no-headers --select 1 "DISTRIB_CODENAME" | xsv select 2

如果使用部分,这将完成工作:

原始输出示例:

$ ./settings
[section]
SETTING_ONE=this is setting one
SETTING_TWO=This is the second setting
ANOTHER_SETTING=This is another setting

Regexp 解析:

$ ./settings | sed -n -E "/^\[.*\]/{s/\[(.*)\]/\1/;h;n;};/^[a-zA-Z]/{s/#.*//;G;s/([^ ]*) *= *(.*)\n(.*)/\3_\1='\2'/;p;}"
section_SETTING_ONE='this is setting one'
section_SETTING_TWO='This is the second setting'
section_ANOTHER_SETTING='This is another setting'

现在大家一起来:

$ eval "$(./settings | sed -n -E "/^\[.*\]/{s/\[(.*)\]/\1/;h;n;};/^[a-zA-Z]/{s/#.*//;G;s/([^ ]*) *= *(.*)\n(.*)/\3_\1='\2'/;p;}")"
$ echo $section_SETTING_TWO
This is the second setting

我有漂亮的一行程序(假设你已经安装了 phpjq) :

cat file.ini | php -r "echo json_encode(parse_ini_string(file_get_contents('php://stdin'), true, INI_SCANNER_RAW));" | jq '.section.key'

对一行程序 sed 答案的解释。

[section1]
param1=123
param2=345
param3=678


[section2]
param1=abc
param2=def
param3=ghi


[section3]
param1=000
param2=111
param3=222
sed -nr "/^\[section2\]/ { :l /^\s*[^#].*/ p; n; /^\[/ q; b l; }" ./file.ini

要理解这一点,可以更容易地将行的格式设置为:

sed -nr "
# start processing when we found the word \"section2\"
/^\[section2\]/  { #the set of commands inside { } will be executed
#create a label \"l\"  (https://www.grymoire.com/Unix/Sed.html#uh-58)
:l /^\s*[^#].*/ p;
# move on to the next line. For the first run it is the \"param1=abc\"
n;
# check if this line is beginning of new section. If yes - then exit.
/^\[/ q
#otherwise jump to the label \"l\"
b l
}


" file.ini

这个线程没有足够的解决方案可供选择,因此这里我的解决方案不需要像 sedawk这样的工具:

grep '^\[section\]' -A 999 config.ini | tail -n +2  | grep -B 999 '^\[' | head -n -1 | grep '^key' | cut -d '=' -f 2

如果您期望的部分超过999行,请随意修改上面的示例。注意,您可能希望修剪结果值,以删除该值后面的空格或注释字符串。如果您需要匹配不从行首开始的键,请删除 ^,如问题的示例所示。在这种情况下,更好的做法是显式地匹配空格和制表符。

如果在给定部分中有多个值要读取,但希望避免多次读取该文件:

CONFIG_SECTION=$(grep '^\[section\]' -A 999 config.ini | tail -n +2  | grep -B 999 '^\[' | head -n -1)


KEY1=$(echo ${CONFIG_SECTION} | tr ' ' '\n' | grep key1 | cut -d '=' -f 2)
echo "KEY1=${KEY1}"
KEY2=$(echo ${CONFIG_SECTION} | tr ' ' '\n' | grep key2 | cut -d '=' -f 2)
echo "KEY2=${KEY2}"

还有一种使用 awk的实现,它的灵活性更高一些。

function parse_ini() {
cat /dev/stdin | awk -v section="$1" -v key="$2" '
BEGIN {
if (length(key) > 0) { params=2 }
else if (length(section) > 0) { params=1 }
else { params=0 }
}
match($0,/#/) { next }
match($0,/^\[(.+)\]$/){
current=substr($0, RSTART+1, RLENGTH-2)
found=current==section
if (params==0) { print current }
}
match($0,/(.+)=(.+)/) {
if (found) {
if (params==2 && key==$1) { print $3 }
if (params==1) { printf "%s=%s\n",$1,$3 }
}
}'
}

您可以使用0到2参数之间的调用传递:

cat myfile1.ini myfile2.ini | parse_ini # List section names


cat myfile1.ini myfile2.ini | parse_ini 'my-section' # Prints keys and values from a section


cat myfile1.ini myfile2.ini | parse_ini 'my-section' 'my-key' # Print a single value

这是基于 Juarez Rudsatz 的回答,所以非常感谢你为我指明了正确的方向。

不过在 Ubuntu 20.04 bash 中我确实遇到了一点小麻烦。

function parse_ini() {
cat /dev/stdin | awk -v section="$1" -v key="$2" '
BEGIN {
if (length(key) > 0) { params=2 }
else if (length(section) > 0) { params=1 }
else { params=0 }
}
match($0,/;/) { next }
match($0,/#/) { next }
match($0,/^\[(.+)\]$/){
current=substr($0, RSTART+1, RLENGTH-2)
found=current==section
if (params==0) { print current }
}
match($0,/(.+)=(.+)/) {
if (found) {
if (params==2 && key==substr($1, 0, length(key))) { print substr($0, length(key)+2) }
if (params==1) { printf "%s\n",$1,$3 }
}
}'
}

这里的区别在于最后一个块中的 subr,Juarez 示例在显示节参数时添加了一个额外的 = 。在输出个人期权时,它也未能展示出任何东西。

为了补救额外的 = I 从 params = = 1行中删除了 =% s。 为了补救丢失的值,我几乎完全重写了 params = = 2行。 我添加了一个匹配来排除; 还添加了注释行。

此示例将处理 INI 文件,如

[default]
opt=1
option=option
option1=This is just another example
example=this wasn't working before
#commented=I will not be shown
;commentedtoo=Neither Will I.


[two]
opt='default' 'opt'just doesnt get me! but 'two' 'opt' does :)
iam=not just for show

使用这个脚本与 Juarez 的例子没有什么不同。

cat options.ini | parse_ini                     # Show Sections
cat options.ini | parse_ini 'default'           # Show Options with values
cat options.ini | parse_ini 'default' 'option'  # Show Option Value
cat options.ini | parse_ini 'two' 'iam'         # Same as last but from another section

这当然是按预期运作的。那么还有什么,啊,是的,你可能想要一个函数文件来整理你的脚本,你可能想要访问这些值。这里有一个简单的例子。

app="$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)" # get the path to your program
source $app/functions.script # could be in a subdirectory.
somevar=$(cat $app/options.ini | parse_ini 'section' 'option') # Now its in a variable.

我将 Chris 编写的函数转换为 shell 脚本供个人使用。你可以自由使用。谢谢,克里斯!

Https://github.com/tyler-hansen/general_use

我提供了一个可执行的二进制和源代码,以及如何使用它们。

read-ini [arg1] [arg2] [arg3]
Arguments:
arg1 = config.ini file
arg2 = section (defined in ini file with [])
arg3 = variable key (within the section)

例如:

 $ read-ini config.ini FANTOM path
/data/hodges_lab/ATAC-STARR_B-cells/data/hansen-fong/fantom