¿Cómo puedo analizar un archivo ini cuyos valores pueden contener ciertos caracteres?

¿Cómo puedo analizar un archivo ini cuyos valores pueden contener ciertos caracteres?

He mirado un par de scripts de análisis de bash ini y he vistoesteUno se usó varias veces aquí, así que estoy tratando de ver si funciona para mí. Parece que lee el archivo ini línea por línea varias veces y con cada pasada construye progresivamente una función que finalmente se evalúa. Funciona bien para algunos caracteres especiales pero no para otros. Si un valor en el archivo contiene una comilla simple o un símbolo mayor/menor que, el script devuelve errores de sintaxis. Otros símbolos también crean resultados inesperados. ¿Cómo puedo manejar estos personajes a medida que los encuentro?

Esta es la función que analiza el ini.

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

archivo ini

[Section1]
value1=abc`def # unexpected EOF while looking for matching ``'
value2=ghi>jkl # syntax error near unexpected token `>'
value3=mno$pqr # executes ok but outputs "mnoqr"
value4=stu;vwx # executes ok but outputs "stu"

Respuesta1

El hecho de que tupoderhacer algo bashno significa que túdebería.

sh(y bashetc.) los scripts son más adecuados para ser contenedores relativamente simples para iniciar programas o comandos de procesamiento de texto. Para tareas más complicadas, incluido analizar archivos ini y actuar sobre ellos, otros lenguajes son más apropiados. ¿Has considerado escribir tu guión en perlo python? Ambos tienen buenos analizadores de archivos .ini: he usado Config::INIel módulo de Perl varias veces cuando necesitaba analizar un archivo ini.

Pero si insistes en hacerlo en bash, deberías usar una matriz asociativa en lugar de configurar variables individuales.

Comience con algo como esto:

#! /bin/bash

inifile='user1074170.ini' 

# declare $config to be an associative array
declare -A config

while IFS='=' read -r key val ; do 
    config["$key"]="$val"
done <  <(sed -E -e '/^\[/d
                     s/#.*//
                     s/[[:blank:]]+$|^[[:blank:]]+//g' "$inifile" )

# now print out the config array
set | grep '^config='

El sedscript elimina la [Section1]línea (en realidad, todas las líneas que comienzan con un corchete abierto [; querrás manejar esto de manera diferente [1] en un archivo ini con múltiples secciones) y elimina los comentarios, así como los espacios en blanco iniciales y finales. El whilebucle lee cada línea, utilizándola =como delimitador de campo, y asigna el contenido a las variables $key y $val, que luego se agregan a la matriz $config.

Producción:

config=([value1]="abc\`def" [value3]="mno\$pqr" [value2]="ghi>jkl" [value4]="stu;vwx" )

Puede usar las entradas de la matriz más adelante en su secuencia de comandos de esta manera:

$ echo value1 is "${config[value1]}"
value1 is abc`def

$ [ "${config[value4]}" = 'stu;vwx' ] && echo true
true

[1] awko perltener formas convenientemente fáciles de leer archivos en modo "párrafo". Un párrafo se define como un bloque de texto separado de otros bloques de texto por una o más líneas en blanco.

Por ejemplo, para trabajar solo con [Section1], inserte el awksiguiente script inmediatamente antes de que el sedscript ingrese al whilebucle anterior:

awk -v RS= -v ORS='\n\n' '/\[Section1\]/' "$inifile" | sed ...

(y eliminar "$inifile"desde el final de la sedlínea de comando, por supuesto; no desea volver a introducir el archivo después de haberse tomado la molestia de extraer solo [Section1]de él).

La configuración ORSno es estrictamente necesaria si solo extrae una sección del archivo ini, pero será útil para mantener la separación de párrafos si extrae dos o más secciones.

Respuesta2

Sé que es una respuesta incompleta, pero MySQL.lnsin augeas parece poder analizar la mayor parte de eso. En augtool:

augtool> set /augeas/load/testini/incl "/root/test.ini"
augtool> set /augeas/load/testini/lens "MySQL.lns"
augtool> load
augtool> ls /files/root/
.ssh/      test.ini/
augtool> ls /files/root/test.ini
target/ = Section1
augtool> ls /files/root/test.ini/target/
value1/ = abc`def
value2/ = ghi>jkl
value3/ = mno$pqr
value4/ = stu

El único en el que se equivocó es el último y, por cierto, no creo que sea un error. En .inilos archivos, el punto y coma marca el comienzo de un comentario. También me gustaría preguntar si sus datos realmente se ven así.

Si es así, puede hacer algo sedantes que establezca ;algún valor de carácter no utilizado y luego transformarlo nuevamente en el posprocesamiento. Sin embargo, en última instancia, necesitará algunos estándares para que el archivo pueda tener una estructura discernible.

EDITAR:

Lo probé con la lente PHP y obtuve todo siempre que se citaran los valores:

[root@vlzoreman ~]# augtool
augtool> set /augeas/load/testini/lens "PHP.lns"
augtool> set /augeas/load/testini/incl "/root/test.ini"
augtool> load
augtool>  ls /files/root/test.ini/Section1/
value1 = abc`def
value2 = ghi>jkl
value3 = mno$pqr
value4 = stu;vwx

De lo contrario, llegó tan lejos como llegó la lente MySQL.

EDITAR #2:

Estoy seguro de que hay una forma más clara de escribir esto, pero este es un ejemplo de uso:

[root@vlp-foreman ~]# bash bash.sh
Values for: Section1:
        :: value1 is abc`def
        :: value2 is ghi>jkl
        :: value3 is mno$pqr
        :: value4 is stu;vwx
Values for: Section2:
        :: value1 is abc`def

El guión es:

#!/bin/bash

sections=$(augtool -A --transform "PHP.lns incl /root/test.ini" ls /files/root/test.ini | cut -f1 -d/)

for currentSection in $sections; do

  echo "Values for: $currentSection:"

  fields=$(augtool -A --transform "PHP.lns incl /root/test.ini" ls /files/root/test.ini/$currentSection | awk '{print $1}')

  for currentField in $fields; do

    currentValue=$(augtool -A --transform "PHP.lns incl /root/test.ini" print /files/root/test.ini/$currentSection/$currentField | cut -f2 -d=)
    currentValue=$(echo $currentValue | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//' | sed -e 's/^"//' -e 's/"$//')

    echo -e "\t:: $currentField is $currentValue"

  done

done

Respuesta3

echa un vistazo a crudini:https://www.pixelbeat.org/programs/crudini/
en Ubuntu puedes instalarlo para sudo apt install crudini
leer un valor del archivo ini, ejecuta:

$ value1=$(crudini --get "crudini.ini" "Section1" "value1")

crudini admite varios formatos de archivos ini y maneja caracteres especiales:

crudini.ini

[Section1]
value1=abc`def
value2=ghi>jkl
value3=mno$pqr
value4=stu;vwx

valores de lectura

$ for i in {1..4}; do crudini --get "crudini.ini" "Section1" "value$i"; done
abc`def
ghi>jkl
mno$pqr
stu;vwx
$

información relacionada