Como faço para pegar um valor INI dentro de um script de shell?

Eu tenho um arquivo parameters.ini, como:

[parameters.ini] database_user = user database_version = 20110611142248 

Eu quero ler e usar a versão do database especificada no arquivo parameters.ini de dentro de um script de shell bash para que eu possa processá-lo.

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

Como eu faria isso?

Que tal grepping para essa linha, em seguida, usando o awk

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

Você pode usar o analisador nativo bash para interpretar valores ini, por:

 $ source <(grep = file.ini) 

Arquivo de amostra:

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

Para acessar variables, basta imprimi-las: echo $var1 . Você também pode usar arrays como mostrado acima ( echo ${IPS[@]} ).

Se você quer apenas um único valor, apenas grep para ele:

 source <(grep var1 file.ini) 

É simples, pois você não precisa de nenhuma biblioteca externa para analisar os dados, mas ela apresenta algumas desvantagens. Por exemplo:

  • Se você tem espaços entre = (nome e valor da variável), então você deve cortar os espaços primeiro, por exemplo

     $ source <(grep = file.ini | sed 's/ *= */=/g') 
  • Para apoiar ; comentários, substitua-os por # :

     $ sed "s/;/#/g" foo.ini | source /dev/stdin 
  • As seções não são suportadas (por exemplo, se você tiver [section-name] , então você tem que filtrá-lo como mostrado acima, por exemplo, grep = ), o mesmo para outros erros inesperados.

    Se você precisar ler um valor específico em uma seção específica, use grep -A , sed , awk ou ex ).

    Por exemplo

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

    Nota: Onde -A5 é o número de linhas a serem lidas na seção. Substitua source por cat para depurar.

  • Se você tem algum erro de análise, ignore-o adicionando: 2>/dev/null

Veja também: Como analisar e converter o arquivo ini em variables ​​do array bash? no serverfault SE

Bash não fornece um analisador para esses arquivos, obviamente você pode usar um código awk ou algumas chamadas sed, mas se você for bash-priest e não quiser usar nada mais, então você pode tentar o seguinte código obscuro:

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

Uso:

 # 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" 

O Inseparador Bash pode ser encontrado no blog The Old School DevOps .

Apenas inclua seu arquivo .ini no corpo do bash:

Arquivo example.ini :

 DBNAME=test DBUSER=scott DBPASSWORD=tiger 

Exemplo de arquivo.sh

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

Sed one-liner, que leva as seções em consideração. Arquivo de exemplo:

 [section1] param1=123 param2=345 param3=678 [section2] param1=abc param2=def param3=ghi [section3] param1=000 param2=111 param3=222 

Digamos que você queira o param2 da seção 2. Execute o seguinte:

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

Darei à você

 def 

uma das mais possíveis soluções

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

Todas as soluções que vi até agora também atingiram linhas comentadas. Este não, se o código do comentário é ; :

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

Exibe o valor de my_key em um my_file ini-style:

 sed -n -e 's/^\s*my_key\s*=\s*//p' my_file 
  • -n – não imprime nada por padrão
  • -e – executa a expressão
  • s/PATTERN//p – exibe qualquer coisa seguindo este padrão No padrão:
  • ^ – o padrão começa no começo da linha
  • \s – caractere de espaço em branco
  • * – zero ou muitos (caracteres em branco)

Exemplo:

 $ 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 

Assim:

Encontre um padrão onde a linha começa com zero ou muitos caracteres de espaços em branco, seguidos pela string my_key , seguidos por zero ou muitos caracteres de espaço em branco, um sinal de igual, zero ou muitos caracteres de espaço em branco novamente. Exibe o restante do conteúdo nessa linha seguindo esse padrão.

Para pessoas (como eu) que querem ler arquivos INI a partir de scripts de shell (leia shell, não bash) – criei uma biblioteca de ajuda que tenta fazer exatamente isso:

https://github.com/wallyhall/shini (licença MIT, faça com ele como quiser. Eu vinculei acima, incluindo-o como o código é bastante extenso).

É um pouco mais “complicado” do que as linhas sed simples sugeridas acima – mas funciona de forma muito semelhante.

A function lê um arquivo linha por linha – procurando por marcadores de seção ( [section] ) e declarações de chave / valor ( key=value ).

Em última análise, você recebe um retorno de chamada para sua própria function – seção, chave e valor.

Você pode usar a ferramenta crudini para obter valores ini, por exemplo:

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

Você pode usar o sed para analisar o arquivo de configuração ini, especialmente quando você tem nomes de seção como:

 # 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 

então você pode usar o seguinte script sed para analisar os dados acima:

 # 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 } 

isso converterá os dados ini neste formato simples:

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

Assim, será mais fácil analisar usando sed , awk ou read , tendo nomes de seção em todas as linhas.

Créditos e fonte: Arquivos de configuração para scripts de shell , Michael Grünewald

Algumas das respostas não respeitam comentários. Alguns não respeitam seções. Alguns reconhecem apenas uma syntax (apenas “:” ou apenas “=”). Algumas respostas do Python falham em minha máquina devido à diferente capitalização ou falha ao importar o módulo sys. Todos são um pouco demasiado concisos para mim.

Então eu escrevi o meu próprio, e se você tem um Python moderno, você provavelmente pode chamar isso de seu shell Bash. Ele tem a vantagem de aderir a algumas convenções comuns de codificação do Python e até fornece mensagens de erro e ajuda sensatas. Para usá-lo, nomeie-o como myconfig.py (NÃO o chame de configparser.py ou tente importá-lo,) torne-o executável e chame-o como

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

Aqui está o meu código para o Python 3.5 no Linux:

 #!/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)) 

Aqui está minha versão, que analisa seções e preenche um array associativo global g_iniProperties com ele. Observe que isso funciona apenas com o bash v4.2 e superior.

 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 } 

E aqui está um exemplo de código usando a function acima:

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

Este script irá obter parâmetros da seguinte forma:

o que significa que se o seu ini tem:

pars_ini.ksh

por exemplo. como chamá-lo:


[ meio Ambiente ]

a = x

[DataBase_Sector]

DSN = alguma coisa


Então chamando:

pars_ini.ksh /users/bubu_user/parameters.ini DataBase_Sector DSN

isso irá recuperar o seguinte “algo”

o script “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` 

Minha versão do one-liner

 #!/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:]*// 

Acabei de escrever meu próprio analisador. Eu tentei usar vários parser encontrado aqui, nenhum parece funcionar tanto com ksh93 (AIX) e bash (Linux).

É um estilo de programação antigo – analisando linha por linha. Bastante rápido, pois utilizava poucos comandos externos. Um pouco mais lento por causa de todo o eval necessário para o nome dynamic da matriz.

O ini suporta 3 syntaxs especiais:

  • includefile = arquivo ini -> Carrega um arquivo ini adicional. Útil para dividir ini em vários arquivos ou reutilizar alguma parte da configuração
  • includedir = directory -> O mesmo que includefile, mas inclui um diretório completo
  • includesection = section -> Copiar uma seção existente para a seção atual.

Eu usei todas essas syntaxs para ter um arquivo ini bastante complexo e reutilizável. Útil para instalar produtos ao instalar um novo sistema operacional – fazemos muito isso.

Os valores podem ser acessados ​​com $ {ini [$ section. $ Item]}. A matriz deve ser definida antes de chamar isso.

Diverta-se. Espero que seja útil para outra pessoa!

 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" } 

Essa implementação usa o awk e tem as seguintes vantagens:

  1. Só retornará a primeira input correspondente
  2. Ignora as linhas que começam com a ;
  3. Apara os espaços em branco iniciais e finais, mas não os espaços em branco internos

Versão formatada :

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

One-liner :

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

Quando eu uso uma senha em base64, eu coloco o separador “:” porque a string base64 pode ter “=”. Por exemplo (eu uso o ksh ):

 > echo "Abc123" | base64 QWJjMTIzCg== 

Em parameters.ini coloque a pass:QWJjMTIzCg== linha pass:QWJjMTIzCg== e finalmente:

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

Se a linha tiver espaços como "pass : QWJjMTIzCg== " add | tr -d ' ' | tr -d ' ' para cortá-los:

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

Semelhante às outras respostas do Python, você pode fazer isso usando o sinalizador -c para executar uma sequência de instruções do Python fornecidas na linha de comando:

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

Isso tem a vantagem de exigir apenas a biblioteca padrão do Python e a vantagem de não gravar um arquivo de script separado.

simplicidade complexa

arquivo ini

test.ini

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

script bash com leitura e execução

/ 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 

então, em outro script, eu obtenho os resultados do comando e posso usar quaisquer variables ​​dentro

test.sh

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

finalmente da linha de comando a saída é assim

 # ./test.sh value_2 

Eu escrevi um script python rápido e fácil para include no meu script bash.

Por exemplo, seu arquivo ini é chamado food.ini e no arquivo você pode ter algumas seções e algumas linhas:

 [FRUIT] Oranges = 14 Apples = 6 

Copie este pequeno script Python de 6 linhas e salve-o como configparser.py

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

Agora, no seu script bash, você poderia fazer isso, por exemplo.

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

ou

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

Isto prepose:

  1. você tem o Python instalado
  2. você tem a biblioteca configparser instalada (isto deve vir com uma instalação std python)

Espero que ajude : ¬)

Isso usa o sistema perl e limpa expressões regulares:

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