Journal Lire de fichiers de configuration depuis un script shell

Posté par  (site web personnel) . Licence CC By‑SA.
Étiquettes :
25
5
juin
2015

La petite technique shell du vendredi. Pour lire un fichier de configuration du style INI depuis un script shell, on peut utiliser le petit script sed suivant

1 {
  x
  s/^/default/
  x
}

/^#/n

/^\[/ {
  s/\[\(.*\)\]/\1/
  x
  b
}

/=/ {
  s/^[[:space:]]*//
  s/[[:space:]]*=[[:space:]]*/|/
  G
  s/\(.*\)\n\(.*\)/\2|\1/
  p
}

Il transforme

    # 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

en

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

et ce deuxième format est facile à lire avec awk ou read par exemple.

Avec le shell l'approche classique consiste à sourcer un script contenant des définitions de variables, ce qui peut impliquer des trous de sécurité et n'offre pas beaucoup de possibilités de structuration.

Je le raconte (en pareil et en anglais) sur mon blog.

  • # sed, c'est dien !

    Posté par  (site web personnel) . Évalué à 10.

  • # Perl r0x

    Posté par  (site web personnel) . Évalué à 10.

    Petite version rapide en Perl:

    perl -ne '/^\s*\[(.*?)]/ and $c=$1; /^\s*([^=]+)=(.*)/ and print "$c|$1|$2\n"; ' < fichier.ini

    • [^] # Re: Perl r0x

      Posté par  (site web personnel) . Évalué à 3. Dernière modification le 06 juin 2015 à 10:09.

      Ça me donne surtout envie d'utiliser un autre langage :(

      • [^] # Re: Perl r0x

        Posté par  (site web personnel) . Évalué à 7.

        C'est franchement difficile d'être plus clair pourtant ! On a juste deux commandes, une qui pose le contexte, une qui fait la sortie. C'est juste une petite merveille.

        Il faut savoir être ouvert sur les autres tribus et reconnaître à chacun ses mérites ;-)

        • [^] # Re: Perl r0x

          Posté par  (site web personnel) . Évalué à 2.

          Honnêtement, je ne comprends rien à ce que ça fait, et pourtant j'ai fait un (tout petit) peu de Perl il y a quelques années. Là, je ne saurais même pas dire ce que ça donne en pratique.

          A contrario, j'ai déjà réussi à patcher du Ruby sans jamais en avoir fait.

          • [^] # Re: Perl r0x

            Posté par  (site web personnel) . Évalué à 6.

            Attention, c'est du one-liner, l'objectif est principalement ici d'écrire du code jetable et celui-ci est particulièrement lisible malgré ce que tu dis. Ceci dis, si tu n'y comprends rien, je pense qu'une petite plongée dans Perl ne peut alors que faire du bien histoire d'ouvrir de nouveau paradigme ;-) Bref, c'est une bête machine à état basée sur deux expressions rationnelles assez simples. Il y a pleins de one-liner incompréhensibles (c'est aussi un jeu) mais honnêtement, pas celle-là ;-)

      • [^] # Re: Perl r0x

        Posté par  . Évalué à 2.

        J'ai trop souvent l'impression que Perl est jugé sur ses variables explicites et l'intégration forte des expressions régulières (qui peuvent rendre le code peu lisible, certes).
        Mais pour faire une analogie douteuse, c'est un peu comme dire que le chinois c'est incompréhensible car ce n'est pas un alphabet latin.

        Petit patch:

        perl -ne '/^\s*\[(.*?)]/ and $c=$1; /^\s*([^=#]+)=(.*)/ and print "$c|$1|$2\n"; ' < fichier.ini

        • [^] # Re: Perl r0x

          Posté par  (site web personnel) . Évalué à 8.

          J'ai trop souvent l'impression que Perl est jugé sur ses variables explicites et l'intégration forte des expressions régulières (qui peuvent rendre le code peu lisible, certes).

          Le truc amusant c'est que cette intégration rend, paradoxalement, les expressions régulières potentiellement bien plus lisibles : moins d'échappements à faire, possibilité de les écrire sur plusieurs lignes avec des commentaires et support possible de la part de l'éditeur de texte pour la coloration (vu que ce n'est pas juste des chaîne de caractères ordinaires), auxquels on peut ajouter le fait d'avoir certains messages d'erreurs dans des regex à la compilation et non à l'exécution.

          Je pense que Perl est plutôt généralement jugé sur la possibilité d'écrire des one-liner ou petits scripts très compacts, car c'est souvent la partie la plus visible pour celui qui ne connaît pas Perl, celle qu'il a le plus de chances de rencontrer en tombant sur un forum au hasard, et, malheureusement, c'est probablement une des disciplines qui montrent les parties les moins évidentes et implicites du langage. À côté, le code Perl d'un développement un peu plus gros est beaucoup plus accessible.

  • # Vraiment en shell

    Posté par  . Évalué à 4.

    Ou alors, en restant vraiment en shell (bash 4.0+):

    #------------------------------------------------------------------------------
    # Parses a .ini file
    #
    # License:
    #   GPLv2
    #
    # Parse the specified .ini file, and stores the results in caller-supplied
    # variable arrays:
    #
    # * +$sections[]+: indexed array
    # * +$variables[]+: associative array
    # * +$values[]+: associative array
    # * +$lines[]+: associative array
    #
    # Those variables will be filled as thus:
    #
    #     sections=( [0]='section-0' [1]='section-1' )
    #     variables=( ['section-0']='var-0,var-1' ['section-1']='var-10,var-11' [...] )
    #     values=( ['section-0:var-0']='value-0-0' ['section-1:var-10']='value-1-10' [...] )
    #     lines=( ['section-0']='file:27' ['section-1:var-10']='file:42' [...] )
    #
    # Note that the accepted syntax is a subset of the .ini format, where only
    # '=' is accepted to assign variables, while ':' is not accepted.
    #
    # Param:
    #   $1: .ini file to parse
    #
    # Return:
    #   $sections[] : the list of sections, one section per array index
    #   $variables[]: the comma-separated list of variables for a section,
    #                 indexed by the name of the section
    #   $values[]   : the value of a variable in a section, indexed by the
    #                 'section:variable' tuple
    #   $lines[]    : the 'file:line' a section or variable was defined on,
    #                 indexed by the 'section' name, or the 'section:variable'
    #                 tuple
    #
    parse_ini() {
        local ini_file="${1}"
        local line var val i
        local cur_section
    
        i=0
        while read line; do
            : $((i++))
    
            # Mangle the line:
            # - get rid of comments
            # - get rid of spaces around the first '='
            line="$( sed -r -e 's/[[:space:]]*#.*$//; //d;'                     \
                            -e 's/=/ = /;' -e 's/[[:space:]]+=[[:space:]]+/=/'  \
                            <<<"${line}" )"
    
            case "${line}" in
            "") continue;;
            '['*']')
                cur_section="$( sed -r -e 's/[][]//g;' <<<"${line}" )"
                sections+=( "${cur_section}" )
                lines+=( ["${cur_section}"]="${ini_file}:${i}" )
                continue
            ;;
            ?*=*)   ;;
            *)      printf "malformed entry '%s' in '%s:%d'\n" "${line}" "${ini_file}" ${i} >&2; return 1;;
            esac
    
            var="${line%%=*}"
            eval val="${line#*=}"
            variables+=( ["${cur_section}"]=",${var}" )
            values+=( ["${cur_section}:${var}"]="${val}" )
            lines+=( ["${cur_section}:${var}"]="${ini_file}:${i}" )
        done <"${ini_file}"
    }

    Hop,
    Moi.

    • [^] # Re: Vraiment en shell

      Posté par  (site web personnel) . Évalué à 5.

      Si tu restes en pure bash, tu ne mets pas de $( ) … La première qu'on voit dans ton script sont les sous commandes sed ! Bref, la monoligne Perl est vraiment plus esthétique et rapide. Si tu veux des variables à la fin, à peine modifié, tu fait un eval de son résultat.

    • [^] # Re: Vraiment en shell

      Posté par  (site web personnel) . Évalué à 2.

      Ce n'est pas du bash pur car tu appelles une ou deux fois sed par ligne lue, et cela ne fait pas du tout la même chose car tu ne produis pas de donnée tabulaire. Remarque, je ne programme pas en bash car c'est tout plein de bugs — dès que le job control devient compliqué, cela fait des segfaults.

  • # pure bash et un peu long

    Posté par  . Évalué à -1.

    j'ai utilisé

    #
    # Copyright (c) 2009    Kevin Porter / Advanced Web Construction Ltd
    #                       (http://coding.tinternet.info, http://webutils.co.uk)
    # Copyright (c) 2010-2012     Ruediger Meier <sweet_f_a@gmx.de>
    #                             (https://github.com/rudimeier/)
    #
    # Simple INI file parser.
    #
    # See README for usage.
    #
    #
    
    function read_ini()
    {
        # Be strict with the prefix, since it's going to be run through eval
        function check_prefix()
        {
            if ! [[ "${VARNAME_PREFIX}" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]] ;then
                echo "read_ini: invalid prefix '${VARNAME_PREFIX}'" >&2
                return 1
            fi
        }
    
        function check_ini_file()
        {
            if [ ! -r "$INI_FILE" ] ;then
                echo "read_ini: '${INI_FILE}' doesn't exist or not" \
                    "readable" >&2
                return 1
            fi
        }
    
        # enable some optional shell behavior (shopt)
        function pollute_bash()
        {
            if ! shopt -q extglob ;then
                SWITCH_SHOPT="${SWITCH_SHOPT} extglob"
            fi
            if ! shopt -q nocasematch ;then
                SWITCH_SHOPT="${SWITCH_SHOPT} nocasematch"
            fi
            shopt -q -s ${SWITCH_SHOPT}
        }
    
        # unset all local functions and restore shopt settings before returning
        # from read_ini()
        function cleanup_bash()
        {
            shopt -q -u ${SWITCH_SHOPT}
            unset -f check_prefix check_ini_file pollute_bash cleanup_bash
        }
    
        local INI_FILE=""
        local INI_SECTION=""
    
        # {{{ START Deal with command line args
    
        # Set defaults
        local BOOLEANS=1
        local VARNAME_PREFIX=INI
        local CLEAN_ENV=0
    
        # {{{ START Options
    
        # Available options:
        #   --boolean       Whether to recognise special boolean values: ie for 'yes', 'true'
        #                   and 'on' return 1; for 'no', 'false' and 'off' return 0. Quoted
        #                   values will be left as strings
        #                   Default: on
        #
        #   --prefix=STRING String to begin all returned variables with (followed by '__').
        #                   Default: INI
        #
        #   First non-option arg is filename, second is section name
    
        while [ $# -gt 0 ]
        do
    
            case $1 in
    
                --clean | -c )
                    CLEAN_ENV=1
                ;;
    
                --booleans | -b )
                    shift
                    BOOLEANS=$1
                ;;
    
                --prefix | -p )
                    shift
                    VARNAME_PREFIX=$1
                ;;
    
                * )
                    if [ -z "$INI_FILE" ]
                    then
                        INI_FILE=$1
                    else
                        if [ -z "$INI_SECTION" ]
                        then
                            INI_SECTION=$1
                        fi
                    fi
                ;;
    
            esac
    
            shift
        done
    
        if [ -z "$INI_FILE" ] && [ "${CLEAN_ENV}" = 0 ] ;then
            echo -e "Usage: read_ini [-c] [-b 0| -b 1]] [-p PREFIX] FILE"\
                "[SECTION]\n  or   read_ini -c [-p PREFIX]" >&2
            cleanup_bash
            return 1
        fi
    
        if ! check_prefix ;then
            cleanup_bash
            return 1
        fi
    
        local INI_ALL_VARNAME="${VARNAME_PREFIX}__ALL_VARS"
        local INI_ALL_SECTION="${VARNAME_PREFIX}__ALL_SECTIONS"
        local INI_NUMSECTIONS_VARNAME="${VARNAME_PREFIX}__NUMSECTIONS"
        if [ "${CLEAN_ENV}" = 1 ] ;then
            eval unset "\$${INI_ALL_VARNAME}"
        fi
        unset ${INI_ALL_VARNAME}
        unset ${INI_ALL_SECTION}
        unset ${INI_NUMSECTIONS_VARNAME}
    
        if [ -z "$INI_FILE" ] ;then
            cleanup_bash
            return 0
        fi
    
        if ! check_ini_file ;then
            cleanup_bash
            return 1
        fi
    
        # Sanitise BOOLEANS - interpret "0" as 0, anything else as 1
        if [ "$BOOLEANS" != "0" ]
        then
            BOOLEANS=1
        fi
    
    
        # }}} END Options
    
        # }}} END Deal with command line args
    
        local LINE_NUM=0
        local SECTIONS_NUM=0
        local SECTION=""
    
        # IFS is used in "read" and we want to switch it within the loop
        local IFS=$' \t\n'
        local IFS_OLD="${IFS}"
    
        # we need some optional shell behavior (shopt) but want to restore
        # current settings before returning
        local SWITCH_SHOPT=""
        pollute_bash
    
        while read -r line || [ -n "$line" ]
        do
    
            ((LINE_NUM++))
    
            # Skip blank lines and comments
            if [ -z "$line" -o "${line:0:1}" = ";" -o "${line:0:1}" = "#" ]
            then
                continue
            fi
    
            # Section marker?
            if [[ "${line}" =~ ^\[[a-zA-Z0-9_]{1,}\]$ ]]
            then
    
                # Set SECTION var to name of section (strip [ and ] from section marker)
                SECTION="${line#[}"
                SECTION="${SECTION%]}"
                eval "${INI_ALL_SECTION}=\"\${${INI_ALL_SECTION}# } $SECTION\""
                ((SECTIONS_NUM++))
    
                continue
            fi
    
            # Are we getting only a specific section? And are we currently in it?
            if [ ! -z "$INI_SECTION" ]
            then
                if [ "$SECTION" != "$INI_SECTION" ]
                then
                    continue
                fi
            fi
    
            # Valid var/value line? (check for variable name and then '=')
            if ! [[ "${line}" =~ ^[a-zA-Z0-9._]{1,}[[:space:]]*= ]]
            then
                echo "Error: Invalid line:" >&2
                echo " ${LINE_NUM}: $line" >&2
                cleanup_bash
                return 1
            fi
    
    
            # split line at "=" sign
            IFS="="
            read -r VAR VAL <<< "${line}"
            IFS="${IFS_OLD}"
    
            # delete spaces around the equal sign (using extglob)
            VAR="${VAR%%+([[:space:]])}"
            VAL="${VAL##+([[:space:]])}"
            VAR=$(echo $VAR)
    
    
            # Construct variable name:
            # ${VARNAME_PREFIX}__$SECTION__$VAR
            # Or if not in a section:
            # ${VARNAME_PREFIX}__$VAR
            # In both cases, full stops ('.') are replaced with underscores ('_')
            if [ -z "$SECTION" ]
            then
                VARNAME=${VARNAME_PREFIX}__${VAR//./_}
            else
                VARNAME=${VARNAME_PREFIX}__${SECTION}__${VAR//./_}
            fi
            eval "${INI_ALL_VARNAME}=\"\${${INI_ALL_VARNAME}# } ${VARNAME}\""
    
            if [[ "${VAL}" =~ ^\".*\"$  ]]
            then
                # remove existing double quotes
                VAL="${VAL##\"}"
                VAL="${VAL%%\"}"
            elif [[ "${VAL}" =~ ^\'.*\'$  ]]
            then
                # remove existing single quotes
                VAL="${VAL##\'}"
                VAL="${VAL%%\'}"
            elif [ "$BOOLEANS" = 1 ]
            then
                # Value is not enclosed in quotes
                # Booleans processing is switched on, check for special boolean
                # values and convert
    
                # here we compare case insensitive because
                # "shopt nocasematch"
                case "$VAL" in
                    yes | true | on )
                        VAL=1
                    ;;
                    no | false | off )
                        VAL=0
                    ;;
                esac
            fi
    
    
            # enclose the value in single quotes and escape any
            # single quotes and backslashes that may be in the value
            VAL="${VAL//\\/\\\\}"
            VAL="\$'${VAL//\'/\'}'"
    
            eval "$VARNAME=$VAL"
        done  <"${INI_FILE}"
    
        # return also the number of parsed sections
        eval "$INI_NUMSECTIONS_VARNAME=$SECTIONS_NUM"
    
        cleanup_bash
    }

    exemple :

    read_ini "/etc/yum.conf" main --prefix "yum_config"
    echo "--- Depuis le cache yum : ${yum_config__main__cachedir}"
    • [^] # Re: pure bash et un peu long

      Posté par  . Évalué à 10.

      Sérieusement, quasiment 300 lignes pour lire un fichier ini, traiter les quotes à la main, tripoter IFS et shopt; il n'y a vraiment pas de quoi être fier.

      C'est un bel exemple de code complètement non maintenable. En prime chacun y va de sa copie sans jamais faire la moindre mise à jour histoire de variés les bugs d'un code à l'autre.

      • [^] # Re: pure bash et un peu long

        Posté par  (site web personnel) . Évalué à 7. Dernière modification le 06 juin 2015 à 14:07.

        Quand un script shell dépasse les 100 lignes il est temps de basculer sur un vrai^Wautre langage.

        Par exemple en python:

        import configparser
        config = configparser.ConfigParser()
        config.read('/etc/myinifile.cfg')

        Pourquoi réécrire du code pour traiter un format standard (ie INI files), c'est tout l'intérêt de passer par un standard, hériter du travail fait par d'autre sur le sujet.

        Cependant l'exercice reste intéressant pour la beauté du geste et l'exploration des possibilités du shell.
        Dans le cadre de scripting système dans un environnement pro ça me semble en effet une mauvaise idée de construire de trop gros script en pur shell.

        • [^] # Re: pure bash et un peu long

          Posté par  (site web personnel) . Évalué à 1.

          @!#$ devancé de plus d'une heure
          je salivais à l'avance d'étaler ma science python …

          me reste plus que la mauvaise foi unixienne de base :

          ouiiii maiiiiiis python va consommer 15 fois plus de RAM que le shell …

          Dans le cadre de scripting système dans un environnement pro ça me semble en effet une mauvaise idée de construire de trop gros script en pur shell.

          tu as raison mais le shell reste quand même la meilleure glue qui soit sous unix
          de plus sur certain OS difficile à écarter, comme AIX, python n'est installé en standard, même si c'est faisable facilement
          Sinon le couple shell + python c'est quand même le top pour les scripts complexes comme les "exits" de CFT par exemple.

        • [^] # Re: pure bash et un peu long

          Posté par  (site web personnel) . Évalué à 2.

          Tu veux juste dire que Python est un langage déclaratif qui a un certain de module bien fait ;-)

          use Config::Tiny;
          my $Config = Config::Tiny->read('/etc/myinifile.cfg');

        • [^] # Re: pure bash et un peu long

          Posté par  (site web personnel) . Évalué à 5.

          Dans le cadre de scripting système dans un environnement pro ça me semble en effet une mauvaise idée de construire de trop gros script en pur shell.

          Contrairement à plein de croyances, ce n'est pas si difficile d'écrire des programmes complexes, maintenables et relativement gros avec le shell. La clef c'est de ne pas utiliser le shell n'importe comment. En gros il faut éviter le plus possible de maintenir des données complexes dans le shell lui-même, le shell servant juste à lancer des programmes — plus spécifiquement, des filtres — qui vont effectuer les traitements. En général on part de fonctions “prototypes” et de “mockings” pour écrire très rapidement un prototype. Ensuite on étend le prototype et on peut remplacer les traitements trop complexes par des programmes autonomes. Évidemment, il s'agit de faire du bon gros traitement de données pas du tout interactif, pour les programmes interactifs, cette approche ne va pas du tout!

      • [^] # Re: pure bash et un peu long

        Posté par  . Évalué à 1.

        Je pense être le seul à répondre au problème : shell et config type ini ;p

    • [^] # Re: pure bash et un peu long

      Posté par  . Évalué à 3.

      On peut faire plus simple :

      # Load var from INI files
      function load() {
        for propertie in "$@" ; do
          [[ -f "${propertie}" ]] || die "load() : \"${propertie}\" don't exist !"
          while read ; do
            [[ "${REPLY}" =~ ^# || -z "${REPLY}" ]] && continue
            if [[ "${REPLY}" =~ ^\[.*\]$ ]] ; then
              c="${REPLY//[\[\]]/}"
            else
              eval "${c:-default}_${REPLY%%=*}"=\'"${REPLY#*=}"\'
            fi
          done < "${propertie}"
        done
      }

      Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

      • [^] # Re: pure bash et un peu long

        Posté par  (site web personnel) . Évalué à 3.

        Petite correction d'ortho : property pas propertie. Il faudra aussi définir la fonction die.

        • [^] # Re: pure bash et un peu long

          Posté par  . Évalué à 3.

          Voici la fonction die telle que je m'en sert généralement :

          function die() {
            echo "$@" >&2
            exit 1
          }

          Elle n'est AMHA pas nécessaire à comprendre la logique de la méthode.

          Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

        • [^] # Re: pure bash et un peu long

          Posté par  (site web personnel) . Évalué à 2.

          C'est probablement does not exist ou doesn't exist. Mais au passage exist ça fait un peu vocabulaire mathématique et on utilise souvent plutôt no such … ou … not found — par exemple chez moi:

          % ls /nonexistant
          ls: /nonexistant: No such file or directory
          
    • [^] # Re: pure bash et un peu long

      Posté par  (site web personnel) . Évalué à 2.

      Mais ça ne fait pas du tout la même chose que le script sed puisque cela définit tout un tas de variables avec les valeurs assignées, du coup à moins de faire de l'introspection plutôt hasardeuse, tu ne peux pas itérer sur les valeurs définies mais seulement lire les valeurs que tu attends.

      Avec le script sed on peut transformer un fichier INI comme la config de git en donnée tabulaire, du type

      core|repositoryformatversion|0
      core|filemode|true
      core|bare|false
      core|logallrefupdates|true
      remote "origin"|url|git@github.com:michipili/anvil.git
      remote "origin"|fetch|+refs/heads/*:refs/remotes/origin/*
      branch "master"|remote|origin
      branch "master"|merge|refs/heads/master
      

      ce qui permet de sélectionner avec awk toutes les url de remotes par exemple.

      • [^] # Re: pure bash et un peu long

        Posté par  . Évalué à 3.

        Ton format csv est une plaie à utiliser en shell. On peut facilement modifier mon script pour créer des tableaux associatifs dont le nom correspond à la section et la clef le nom de la variable.

        Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

        • [^] # Re: pure bash et un peu long

          Posté par  (site web personnel) . Évalué à 2. Dernière modification le 09 juin 2015 à 09:00.

          Ton format csv est une plaie à utiliser en shell.

          On est régulièrement en désaccord là-dessus. Pour moi les données tabulaires sont à la base de la programmation en shell et je n'écris quasiment que des filtres qui lisent et écrivent des données tabulaires – et font parfois quelques traitement aussi! – bien-sûr cela demande de ne pas écrire de caractères NUL, de CR ou PIPE dans les champs, mais dans mon cas ce n'est pas une limitation.

          Je suis donc très content avec les données tabulaires. Quels sont les problèmes qu'elles te posent?

          • [^] # Re: pure bash et un peu long

            Posté par  . Évalué à 3.

            Quels sont les problèmes qu'elles te posent?

            Ça se manipule bien avec des sed/grep/awk/perl, mais en shell c'est une infamie.

            Par contre de manière générale, le fait de s'en servir avec des filtres ça sert quand tu cherche à traiter le contenu du fichier INI, quand tu veut t'en servir comme d'une configuration, l'utilisation sous forme tabulaire n'est pas pratique (tu dois de toute manière déserialiser ton fichier).

            Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

            • [^] # Re: pure bash et un peu long

              Posté par  (site web personnel) . Évalué à 2. Dernière modification le 09 juin 2015 à 10:24.

              Ça se manipule bien avec des sed/grep/awk/perl, mais en shell c'est une infamie

              Ma règle d'or de la programmation shell est de traiter aussi peu de données que possible avec le shell que je considère plutôt comme un outil de description des workflows qui transfère des données d'/un processus à l'autre, si possible sans regarder ce qu'il y a dedans.

              quand tu veut t'en servir comme d'une configuration, l'utilisation sous forme tabulaire n'est pas pratique (tu dois de toute manière déserialiser ton fichier).

              Ça dépend un peu des cas d'utilisation: s'il s'agit de lire une batterie de paramètres, je suis complètement d'accord avec toi, cependant si on a une collection de valeurs a priori inconnue (du style git qui a des paramètres pour chaque branche, mais ne connaît pas a priori le nom des branches), alors une donnée tabulaire peut-être pertinente.

              On peut aussi traduire la donnée tabulaire en fichier contenant des affectations puis sourcer ce fichier, au lieu de faire ça “ligne à ligne” ce qui est forcément très bavard (code) et très lent.

              • [^] # Re: pure bash et un peu long

                Posté par  . Évalué à 3.

                Ma règle d'or de la programmation shell est de traiter aussi peu de données que possible avec le shell

                C'est toi que ça regarde.

                que je considère plutôt comme un outil de description des workflows qui transfère des données d'/un processus à l'autre, si possible sans regarder ce qu'il y a dedans.

                Pour utiliser différents outils de workflow (ou affilié) le shell est assez mauvais pour ça (utiliser plus d'entrée et de sortie que ceux prévu en standard n'est pas très pratique, complexité de créer des routes avec plus d'une entrée et plus d'une sortie, aucun mécanisme de monitoring de ce que se passe, difficulté de debugage (beaucoup de monde écris cmd1|cmd2|cmd3 sans s'intéresser à gérer les erreurs de la commande cmd2, etc).

                Bref ce n'est AMHA pas parce qu'il fait des pipes que ça en fait un outil de description de workflow, ou du moins il a à peu près autant de défaut pour décrire des workflow que pour gérer des données.

                Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

                • [^] # Re: pure bash et un peu long

                  Posté par  (site web personnel) . Évalué à 3.

                  C'est toi que ça regarde.

                  Heu, je ne comprends pas trop le sens de cette discussion? J'explique pourquoi je n'ai pas de problème à traiter les données tabulaires avec le shell c'est tout simplement parceque je ne le fais pas – et ce, sciemment. C'est grave d'échanger des points de vue et des expériences?

                  Pour utiliser différents outils de workflow (ou affilié) le shell est assez mauvais pour ça …

                  Effectivement, pour les tâches complexes, je trouve le shell excellent pour écrire des prototypes – plutôt fragiles et lents, mais rapides à écrire.

                • [^] # Re: pure bash et un peu long

                  Posté par  (site web personnel) . Évalué à 2.

                  Pour utiliser différents outils de workflow (ou affilié)

                  Qu'est-ce que tu utilises dont tu es content? J'ai commencé à implémenter une bibliothèque en OCaml pour faire cela et j'aimerais bien regarder l'API d'autres solutions pour m'en inspirer.

  • # En bash, à la rache

    Posté par  . Évalué à -1.

    while read line ; do
        echo $line | grep -qE '^(#|$)' && continue
        if echo $line | grep -q '^\[.*\]$' ; then
            context=$(echo $line | tr -d '[]')
            continue
        fi
        echo -n "$context|"
        echo $line | sed 's/=/|/'
    done < file.ini
  • # En python, c'est quand même plus simple

    Posté par  . Évalué à 2.

    Le fichier de config .ini :

    [DB]
    mysql.db='prout'
    mysql.host='localhost'
    mysql.user='lambda'
    mysql.password='xxxxxxx'
    [CONSOLE]
    cons.port="12443"
    cons.user="lambada"
    cons.password="xxxxxx"
    

    Le code pour parser le fichier et peupler des variables.

    #!/usr/bin/python
    
    import ConfigParser
    
    def parseConfigFile(ini):
    
        config = ConfigParser.ConfigParser()
        try :
            fh = config.readfp(open(ini))
        except Exception as e:
            print str(e)
            raise SystemExit(1)
    
        ## Fetching parameters
        try :
                mysql_db = config.get('DB', 'mysql.db')
                mysql_user = config.get('DB', 'mysql.user')
                mysql_db = config.get('DB', 'mysql.password')
                cons_port = config.get('CONSOLE', 'cons.port')
                cons_user = config.get('CONSOLE', 'cons.user')
                cons_password = config.get('CONSOLE', 'cons.password')
        except Exception as e:
            print str(e)
            raise SystemExit(1)
    
    parseConfigFile('cfig.ini')
    • [^] # Re: En python, c'est quand même plus simple

      Posté par  (site web personnel) . Évalué à 0.

      Ah la vache, c'est caca boudin ! Deux try/except en aussi peu de ligne, aucune variable déclarée… C'est quoi ce langage de daube ;-)

      • [^] # Re: En python, c'est quand même plus simple

        Posté par  . Évalué à 2.

        Premier try/catch :

        il faut bien tester le cas où le fichier n'est pas accessible pour plein de raisons.

        Second try/catch :

        Il faut bien s'assurer que les variables dont le script a besoin pour fonctionner, sont bien présentes dans le fichier de config ini, et de surcroire dans la bonne section (ce qui valide la présence des sections).

        Au contraire, je trouve ça très simple et relativement concis puisqu'on vérifie quand même pas mal de choses en très peu de lignes.

        Il suffit de comparer avec les codes shell précédents….
        Alors ok je m'appuie sur une api, donc une partie du boulot et déjà fait, mais je ne vois pas l'intérêt de réinventer la roue.

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.