如何在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 -A
,sed
,awk
或ex
)。例如
source <(grep = <(grep -A5 '\[section-b\]' file.ini))
注意:其中
-A5
是在该部分中要读取的行数。 用cat
replacesource
进行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
所以使用sed
, awk
parsing会更容易,或者在每行中都有段名称来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
,具有以下优点:
- 只会返回第一个匹配的条目
- 忽略以a开头的行
- 修剪首尾空白,但不是内部空格
格式化版本 :
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
这预示着:
- 你已经安装了Python
- 你已经安装了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