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

我有一个parameters.ini文件,如:

[parameters.ini] database_user = user database_version = 20110611142248 

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

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

我将如何做到这一点?

那么如何使用awk对该行进行grepping

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

Bash不提供这些文件的parsing器,显然你可以使用awk代码或者几个sed调用,但是如果你是bash-priest并且不想使用更多的东西,那么你可以尝试下面的模糊代码:

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

Bash ini-parser可以在The Old School DevOps博客网站find 。

您可以使用bash原生parsing器来解释ini值,方法如下:

 $ source <(grep = file.ini) 

示例文件:

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

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

如果你只想要一个值只是grep:

 source <(grep var1 file.ini) 

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

  • 如果你有= (variables名和值)之间的空格,那么你必须先修剪空格,例如

     $ source <(grep = file.ini | sed 's/ *= */=/g') 
  • 支持; 评论,用#replace它们:

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

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

    例如

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

    注意:其中-A5是在该部分中要读取的行数。 catreplacesource进行debugging。

  • 如果您有任何parsing错误,请通过添加: 2>/dev/null忽略它们

另请参阅: 如何parsing和转换ini文件到bash数组variables? 在serverfault SE

可能的解决scheme之一

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

到目前为止,我所见过的所有解决scheme也都被注释掉了。 这一个没有,如果评论代码是;

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

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; bl;}" ./file.ini 

会给你

 def 

以ini样式的my_file显示my_key的值:

 sed -n -e 's/^\s*my_key\s*=\s*//p' my_file 
  • -n – 默认情况下不打印任何东西
  • -e – 执行expression式
  • s/PATTERN//p – 显示跟随此模式的任何内容在模式中:
  • ^ – 模式从行首开始
  • \s – 空格字符
  • * – 零个或多个(空格字符)

例:

 $ 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 

所以:

find一个模式,其中行以零个或多个空白字符开始,后跟stringmy_key ,后跟零个或多个空白字符,等号,然后是零个或多个空白字符。 按照该模式显示该行上的其余内容。

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

文件example.ini

 DBNAME=test DBUSER=scott DBPASSWORD=tiger 

文件example.sh

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

对于像我这样的人来说,要从shell脚本中读取INI文件(阅读shell,而不是bash) – 我已经打开了一个小小的帮助程序库,试图做到这一点:

https://github.com/wallyhall/shini (麻省理工学院的许可证,请按照你的意愿去做,因为代码相当长,所以我已经链接了上面的代码)。

它比上面提到的简单的sed线条更复杂一些,但是工作原理非常相似。

函数逐行读取文件 – 查找段标记( [section] )和键/值声明( key=value )。

最终你得到callback到你自己的function – 部分,关键和价值。

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

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

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

这意味着如果你的ini有:

pars_ini.ksh <ini文件的path> <Ini文件中扇区的名称> <name中的名称=要返回的值>

例如。 如何调用它:


[ 环境 ]

一个= X

[DataBase_Sector]

DSN =某事


然后打电话:

pars_ini.ksh /users/bubu_user/parameters.ini DataBase_Sector 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` 

您可以使用sed来parsinginiconfiguration文件,特别是当您具有如下部分名称时:

 # 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脚本parsing上面的数据:

 # 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 

所以使用sedawkparsing会更容易,或者在每行中都有段名称来read

Credits&source: shell脚本的configuration文件 ,MichaelGrünewald

有些答案不尊重评论。 有些人不尊重部分。 一些只识别一种语法(只有“:”或只有“=”)。 一些Python答案在我的机器上因为资源不同或者无法导入sys模块而失败。 对我来说,这一切都太简单了。

所以我写了自己的,如果你有一个现代的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)) 

我的版本的单线

 #!/bin/bash #Reader for MS Windows 3.1 Ini-files #Usage: inireader.sh # eg: 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:]*// 

刚刚写完我自己的parsing器。 我试图使用这里发现的各种parsing器,似乎没有用ksh93(AIX)和bash(Linux)。

这是旧的编程风格 – 逐行parsing。 因为它使用了一些外部命令,所以速度很快 有点慢,因为数组的dynamic名称所需的所有eval。

ini支持3种特殊的语法:

  • includefile = ini文件 – >加载额外的ini文件。 用于在多个文件中分割ini,或重新使用一些configuration
  • includedir = directory – >与includefile相同,但包含一个完整的目录
  • includesection = 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. 忽略以a开头的行
  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 

为了什么是值得的…我写了一个快速简单的Python脚本来包含在我的bash脚本中。

例如,你的ini文件叫做food.ini ,在文件中你可以有一些章节和一些行:

 [FRUIT] Oranges = 14 Apples = 6 

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

 #!/usr/bin/python import ConfigParser 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. 你已经安装了Config-Parser库(这应该附带一个std python安装)

希望它有帮助(希望它的作品):¬)

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

 > echo "Abc123" | base64 QWJjMTIzCg== 

parameters.ini pass:QWJjMTIzCg== ,最后:

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

如果该行有空格,如"pass : QWJjMTIzCg== " add | tr -d ' ' | tr -d ' '来修剪它们:

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

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

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

这具有只需要Python标准库的优点,并且不需要编写单独的脚本文件。

复杂简单

ini文件

test.ini

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

bash脚本读取和执行

/斌/ 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 

然后在另一个脚本中,我input命令的结果,并可以使用其中的任何variables

test.sh

 #!/bin/bash source parseini test.ini echo $section2_name2 

最后从命令行输出

 # ./test.sh value_2