Comment puis-je saisir une valeur INI dans un script shell?

J’ai un fichier parameters.ini, tel que:

[parameters.ini] database_user = user database_version = 20110611142248 

Je veux lire et utiliser la version de firebase database spécifiée dans le fichier parameters.ini à partir d’un script shell bash afin que je puisse le traiter.

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

Comment pourrais-je faire ça?

Que diriez-vous de grignoter pour cette ligne en utilisant awk

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

Vous pouvez utiliser un parsingur natif bash pour interpréter les valeurs ini en:

 $ source <(grep = file.ini) 

Fichier d'exemple:

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

Pour accéder aux variables, vous les imprimez simplement: echo $var1 . Vous pouvez également utiliser des tableaux comme indiqué ci-dessus ( echo ${IPS[@]} ).

Si vous ne voulez qu'une seule valeur, grep seulement:

 source <(grep var1 file.ini) 

C'est simple car vous n'avez besoin d'aucune bibliothèque externe pour parsingr les données, mais cela comporte certains inconvénients. Par exemple:

  • Si vous avez des espaces entre = (nom de la variable et valeur), vous devez d'abord couper les espaces, par exemple

     $ source <(grep = file.ini | sed 's/ *= */=/g') 
  • Pour soutenir commentaires, remplacez-les par # :

     $ sed "s/;/#/g" foo.ini | source /dev/stdin 
  • Les sections ne sont pas supscopes (par exemple, si vous avez [section-name] , vous devez le filtrer comme indiqué ci-dessus, par exemple grep = ), même chose pour les autres erreurs inattendues.

    Si vous avez besoin de lire une valeur spécifique dans une section spécifique, utilisez grep -A , sed , awk ou ex ).

    Par exemple

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

    Remarque: où -A5 correspond au nombre de lignes à lire dans la section. Remplacez la source par cat pour déboguer.

  • Si vous avez des erreurs d'parsing, ignorez-les en ajoutant: 2>/dev/null

Voir aussi: Comment parsingr et convertir le fichier ini en variables de tableau bash? à serverfault SE

Bash ne fournit pas d’parsingur pour ces fichiers, vous pouvez évidemment utiliser un code awk ou quelques appels sed, mais si vous êtes bash-priest et que vous ne voulez rien utiliser de plus, vous pouvez essayer le code obscur suivant:

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

Usage:

 # 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 peut être trouvé sur le site du blog The Old School DevOps .

Il suffit d’inclure votre fichier .ini dans le corps de bash:

Fichier example.ini :

 DBNAME=test DBUSER=scott DBPASSWORD=tiger 

Fichier example.sh

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

Sed one-liner, qui prend en compte les sections. Exemple de fichier:

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

Disons que vous voulez param2 de la section2. Exécutez les opérations suivantes:

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

te donnera

 def 

une des solutions les plus possibles

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

Toutes les solutions que j’ai vues jusqu’à présent s’appuient également sur les lignes commentées. Celui-ci n’a pas, si le code de commentaire est ; :

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

Affiche la valeur de my_key dans un ini-style my_file :

 sed -n -e 's/^\s*my_key\s*=\s*//p' my_file 
  • -n – n’imprime rien par défaut
  • -e – exécute l’expression
  • s/PATTERN//p – affiche tout ce qui suit ce motif Dans le motif:
  • ^ – le motif commence au début de la ligne
  • \s – Caractère d’espacement
  • * – zéro ou plusieurs (caractères blancs)

Exemple:

 $ 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 

Alors:

Recherchez un modèle où la ligne commence par zéro ou plusieurs caractères d’ espacement , suivis par la chaîne my_key , suivie de zéro ou de nombreux caractères d’ espacement , d’un signe égal, puis de zéro ou de nombreux caractères d’espacement. Affichez le rest du contenu sur cette ligne en suivant ce modèle.

Pour les personnes (comme moi) qui cherchent à lire des fichiers INI à partir de scripts shell (lire le shell, pas bash) – j’ai créé une petite bibliothèque d’aide qui tente de faire exactement cela:

https://github.com/wallyhall/shini (licence MIT, faites-le comme bon vous semble. J’ai lié ci-dessus en l’incluant car le code est assez long.)

C’est un peu plus “compliqué” que les lignes simples suggérées ci-dessus – mais fonctionne sur une base très similaire.

Fonction lit dans un fichier ligne par ligne – en recherchant les marqueurs de section ( [section] ) et les déclarations clé / valeur ( key=value ).

En fin de compte, vous obtenez un rappel à votre propre fonction – section, clé et valeur.

Vous pouvez utiliser l’outil crudini pour obtenir des valeurs ini, par exemple:

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

Vous pouvez utiliser sed pour parsingr le fichier de configuration ini, en particulier lorsque vous avez des noms de section comme:

 # 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 

vous pouvez donc utiliser le script sed suivant pour parsingr les données ci-dessus:

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

Cela convertira les données ini dans ce format plat:

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

il sera donc plus facile d’parsingr l’utilisation de sed , awk ou read en ayant des noms de section dans chaque ligne.

Crédits & source: fichiers de configuration pour les scripts shell , Michael Grünewald

Certaines des réponses ne respectent pas les commentaires. Certains ne respectent pas les sections. Certains ne reconnaissent qu’une seule syntaxe (seulement “:” ou seulement “=”). Certaines réponses Python échouent sur ma machine en raison de différences de capture ou d’importation du module sys. Tous sont un peu trop laconiques pour moi.

J’ai donc écrit le mien, et si vous avez un Python moderne, vous pouvez probablement l’appeler depuis votre shell Bash. Il a l’avantage d’adhérer à certaines des conventions de codage Python courantes et fournit même des messages d’erreur et une aide judicieux. Pour l’utiliser, nommez-le quelque chose comme myconfig.py (ne l’appelez pas configparser.py ou il peut essayer de s’importer), rendez-le exécutable et appelez-le comme

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

Voici mon code pour Python 3.5 sous 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)) 

Voici ma version, qui parsing les sections et remplit un tableau associatif global g_iniProperties . Notez que cela ne fonctionne qu’avec bash v4.2 et supérieur.

 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 } 

Et voici un exemple de code utilisant la fonction ci-dessus:

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

Ce script obtiendra les parameters suivants:

ce qui signifie que si votre ini a:

pars_ini.ksh

par exemple. comment l’appeler:


[environnement]

a = x

[DataBase_Sector]

DSN = quelque chose


Puis appelant:

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

cela va récupérer le “quelque chose” suivant

le 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` 

Ma version du 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:]*// 

Je viens de finir d’écrire mon propre parsingur. J’ai essayé d’utiliser divers parsingurs trouvés ici, aucun ne semble fonctionner avec ksh93 (AIX) et bash (Linux).

C’est l’ancien style de programmation – parsing ligne par ligne. Assez rapide car il utilise peu de commandes externes. Un peu plus lent à cause de tous les eval requirejs pour le nom dynamic du tableau.

L’ini supporte 3 syntaxes spéciales:

  • includefile = fichier ini -> Charger un fichier ini supplémentaire. Utile pour diviser ini en plusieurs fichiers, ou réutiliser un morceau de configuration
  • includedir = directory -> Identique à includefile, mais inclure un répertoire complet
  • includesection = section -> Copie une section existante dans la section en cours.

J’ai utilisé toute cette syntaxe pour avoir un fichier ini assez complexe et réutilisable. Utile pour installer des produits lors de l’installation d’un nouveau système d’exploitation – nous le faisons souvent.

Les valeurs sont accessibles avec $ {ini [$ section. $ Item]}. Le tableau DOIT être défini avant d’appeler cela.

S’amuser. J’espère que c’est utile pour quelqu’un d’autre!

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

Cette implémentation utilise awk et présente les avantages suivants:

  1. Ne renverra que la première entrée correspondante
  2. Ignore les lignes qui commencent par un ;
  3. Ajuste les espaces de début et de fin, mais pas les espaces internes

Version formatée :

 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 

Lorsque j’utilise un mot de passe en base64, je mets le séparateur “:” car la chaîne base64 peut avoir “=”. Par exemple (j’utilise ksh ):

 > echo "Abc123" | base64 QWJjMTIzCg== 

Dans parameters.ini mettez le pass:QWJjMTIzCg== ligne pass:QWJjMTIzCg== , et enfin:

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

Si la ligne a des espaces comme "pass : QWJjMTIzCg== " add | tr -d ' ' | tr -d ' ' pour les couper:

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

Semblable aux autres réponses Python, vous pouvez le faire en utilisant l’ -c pour exécuter une séquence d’instructions Python sur la ligne de commande:

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

Cela a l’avantage de ne nécessiter que la bibliothèque standard Python et l’avantage de ne pas écrire de fichier script séparé.

simplicité complexe

fichier ini

test.ini

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

script bash avec read et execute

/ 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 

puis, dans un autre script, je recherche les résultats de la commande et peut utiliser toutes les variables

test.sh

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

enfin de la ligne de commande la sortie est donc

 # ./test.sh value_2 

J’ai écrit un script python rapide et facile à inclure dans mon script bash.

Par exemple, votre fichier ini s’appelle food.ini et dans le fichier, vous pouvez avoir des sections et des lignes:

 [FRUIT] Oranges = 14 Apples = 6 

Copiez ce petit script Python 6 lignes et enregistrez-le sous 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]) 

Maintenant, dans votre script bash, vous pouvez le faire par exemple.

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

ou

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

Cela suppose:

  1. vous avez installé Python
  2. vous avez la bibliothèque configparser installée (cela devrait venir avec une installation std python)

J’espère que ça aide : ¬)

Cela utilise le système perl et nettoyer les expressions régulières:

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