Como analisar e converter o arquivo ini em variáveis do array bash?

8

Estou tentando converter um arquivo ini em variáveis de matriz bash. A amostra ini é como abaixo:

[foobar]
session=foo
path=/some/path

[barfoo]
session=bar
path=/some/path

para que estes se tornem:

session[foobar]=foo
path[foobar]=/some/path
session[barfoo]=bar

e assim por diante.

Neste momento, eu poderia criar apenas este comando

awk -F'=' '{ if ($1 ~ /^\[/) section=$1; else if ($1 !~ /^$/) print $1 section "=" $2 }'

Além disso, outro problema é que não são considerados espaços próximos a = . Eu acho que sed é provavelmente mais adequado para este trabalho, mas eu não sei como manter e armazenar uma variável temporária para o nome da seção em sed .

Então, alguma ideia de como fazer isso?

    
por Flint 01.01.2012 / 07:59

5 respostas

6

O Gawk aceita expressões regulares como delimitadores de campo. O seguinte elimina os espaços ao redor do sinal de igual, mas os preserva no restante da linha. As cotas são adicionadas em torno do valor, de modo que esses espaços, se houver algum, são preservados quando a atribuição do Bash é executada. Estou assumindo que os nomes das seções serão variáveis numéricas, mas se você estiver usando o Bash 4, seria fácil adaptá-lo para usar matrizes associativas com os próprios nomes das seções como índices.

awk -F ' *= *' '{ if ($1 ~ /^\[/) section=$1; else if ($1 !~ /^$/) print $1 section "=" "\"" $2 "\"" }'

Note que você também pode querer fazer a remoção de espaço mostrada por Khaled (em apenas $ 1 e seção), pois os nomes das variáveis Bash não podem conter espaços.

Além disso, esse método não funcionará se os valores contiverem sinais de igual.

Outra técnica seria usar um loop Bash while read e executar as atribuições conforme o arquivo é lido, usando declare , que é seguro para a maioria dos conteúdos maliciosos.

foobar=1
barfoo=2  # or you could increment an index variable each time a section is found
while IFS='= ' read var val
do
    if [[ $var == \[*] ]]
    then
        section=$var
    elif [[ $val ]]
    then
        declare "$var$section=$val"
    fi
done < filename

Mais uma vez, os arrays associativos podem ser facilmente suportados.

    
por 01.01.2012 / 13:36
8

Eu usaria um script python simples para esse trabalho, já que ele foi compilado no analisador do INI:

#!/usr/bin/env python

import sys, ConfigParser

config = ConfigParser.ConfigParser()
config.readfp(sys.stdin)

for sec in config.sections():
    print "declare -A %s" % (sec)
    for key, val in config.items(sec):
        print '%s[%s]="%s"' % (sec, key, val)

e depois no bash:

#!/bin/bash

# load the in.ini INI file to current BASH - quoted to preserve line breaks
eval "$(cat in.ini  | ./ini2arr.py)"

# test it:
echo ${barfoo[session]}

Claro, existem implementações mais curtas no awk, mas acho que isso é mais legível e mais fácil de manter.

    
por 18.01.2012 / 16:58
3

Se você quiser eliminar os espaços extras, você pode usar a função interna gsub . Por exemplo, você pode adicionar:

gsub(/ /, "", $1);

Isso removerá todos os espaços. Se você quiser remover espaços no início ou no final do token, use

gsub(/^ /, "", $1);
gsub(/ $/, "", $1);
    
por 01.01.2012 / 09:30
0

Aqui está uma solução pura e simples.

Esta é uma versão nova e melhorada do que o chilladx postou:

link

Para um exemplo inicial muito fácil de seguir: Depois de fazer o download, basta copiar os arquivos bash-ini-parser e scripts/file.ini para o mesmo diretório e criar um script de teste do cliente usando o exemplo fornecido abaixo para esse mesmo diretório também.

source ./bash-ini-parser
cfg_parser "./file.ini"
cfg_section_sec2
echo "var2=$var2"
echo "var5[*]=${var5[*]}"
echo "var5[1]=${var5[1]}"

Aqui estão algumas melhorias que fiz no script bash-ini-parser ...

Se você quiser ler arquivos ini com finais de linha do Windows e Unix, adicione esta linha à função cfg_parser imediatamente após a que lê o arquivo:

ini=$(echo "$ini"|tr -d '\r') # remove carriage returns

Se você quiser ler arquivos que tenham permissões de acesso restritivas, adicione esta função opcional:

# Enable the cfg_parser to read "locked" files
function sudo_cfg_parser {

    # Get the file argument
    file=$1

    # If not "root", enable the "sudo" prefix
    sudoPrefix=
    if [[ $EUID -ne 0 ]]; then sudoPrefix=sudo; fi

    # Save the file permissions, then "unlock" the file
    saved_permissions=$($sudoPrefix stat -c %a $file)
    $sudoPrefix chmod 777 $file

    # Call the standard cfg_parser function
    cfg_parser $file

    # Restore the original permissions
    $sudoPrefix chmod $saved_permissions $file  
}
    
por 21.01.2016 / 17:58
0

Sempre assumindo ter o ConfigParser do Python por perto, pode-se construir uma função auxiliar de shell como esta:

get_network_value()
{
    cat <<EOF | python
import ConfigParser
config = ConfigParser.ConfigParser()
config.read('network.ini')
print (config.get('$IFACE','$param'))
EOF
}

$IFACE e $param são a seção, respectivamente, o parâmetro.

Esse ajudante permite chamadas como:

address='param=address get_network_value' || exit 1
netmask='param=netmask get_network_value' || exit 1
gateway='param=gateway get_network_value' || exit 1

Espero que isso ajude!

    
por 10.05.2018 / 17:08