Melhor maneira de ler um arquivo de configuração no bash

23

Qual é a melhor maneira de ler um arquivo de configuração no bash?
Por exemplo, você tem um script e não está disposto a preencher toda a configuração manualmente sempre que chamar o script.

Edit 1: Eu acho que não deixei claro, então: o que eu quero é ... Eu tenho um arquivo de configuração que é como

variable_name value  
variable_name value ...  

e eu quero ler isso. Eu sei que eu poderia simplesmente cumprimentá-lo pelos argumentos que estou procurando, mais ou menos ... mas talvez haja uma maneira mais inteligente :)

IcyIcyIce
fonte
2
Você terá que mostrar que tipo de configuração você quer dizer. Forneça um exemplo.
Muru
Veja também uma pergunta muito semelhante no StackExchange do Unix e Linux.
Michael

Respostas:

38

Como o mbiber disse, sourceoutro arquivo. Por exemplo, seu arquivo de configuração (por exemplo some.config) seria:

var1=val1
var2=val2

E seu script pode ter a seguinte aparência:

#! /bin/bash

# Optionally, set default values
# var1="default value for var1"
# var1="default value for var2"

. /path/to/some.config

echo "$var1" "$var2"

Os muitos arquivos /etc/defaultgeralmente servem como arquivos de configuração para outros scripts de shell de maneira semelhante. Um exemplo muito comum das postagens aqui é /etc/default/grub. Este arquivo é usado para definir opções de configuração para o GRUB, pois grub-mkconfigé um script de shell que o origina:

sysconfdir="/etc"
#…
if test -f ${sysconfdir}/default/grub ; then
  . ${sysconfdir}/default/grub
fi

Se você realmente deve processar a configuração do formulário:

var1 some value 1
var2 some value 2

Então você pode fazer algo como:

while read var value
do
    export "$var"="$value"
done < /path/to/some.config

(Você também pode fazer algo assim eval "$var=$value", mas isso é mais arriscado do que obter um script. Você pode inadvertidamente quebrá-lo mais facilmente do que um arquivo de origem.)

muru
fonte
Essa é uma ótima maneira, eu poderia usar o espaço em branco substituto sed para "="
IcyIcyIce 8/16
1
@IcyIcyIce Apenas não. Por que você não pode escrever =desde o início?
muru 8/03/16
1
@IcyIcyIce É assim que deveria ser. Você pode usar o código do shell nos vários arquivos /etc/default- e principalmente para um bom uso. Se seu usuário sem noção tiver acesso à configuração para algo executando como root, já é uma causa perdida.
Muru
1
@IcyIcyIce peça a seus professores para esclarecerem o que eles querem. Uma parte do aprendizado do desenvolvimento de software é conhecer claramente os requisitos.
muru 8/03/16
1
@IcyIcyIce ver atualização.
muru 8/03/16
4

Obviamente, eu não sou o bashespecialista aqui, mas o conceito não deve ser diferente no idioma que você usa:

Um exemplo

No exemplo abaixo, você pode usar um script (muito) básico para definir uma sequência ou imprimir uma sequência, conforme definido no seu arquivo de configuração:

#!/bin/bash

# argument to set a new string or print the set string
arg=$1
# possible string as second argument
string=$2
# path to your config file
configfile="$HOME/stringfile"

# if the argument is: "set", write the string (second argument) to a file
if [ "$arg" == "set" ]
then
echo "$string" > $configfile 
# if the argunment is "print": print out the set string, as defined in your file
elif [ "$arg" == "print" ]
then 
echo "$( cat $configfile )"
fi

Então

  • Para definir uma string no seu arquivo de configuração:

    $ '/home/jacob/Bureaublad/test.sh' set "Een aap op een fiets, hoe vind je zoiets?"
  • Posteriormente, para imprimir a string, conforme definido no seu "arquivo de configuração":

    $ '/home/jacob/Bureaublad/test.sh' print
    Een aap op een fiets, hoe vind je zoiets?

Obviamente, em um script aplicado real, você precisa adicionar muitas coisas para garantir que os argumentos estejam corretos, decidir o que fazer quando a entrada estiver incorreta, o arquivo de configurações não existir etc., mas:

Essa é a ideia básica

Jacob Vlijm
fonte
obrigado, eu só estou atualizando a pergunta que eu acho que eu não deixar claro ... mas em casos simples, isso iria funcionar como um encanto ofc
IcyIcyIce
4

Use sourceou .para carregar um arquivo.

source /path/to/file

ou

. /path/to/file

Também é recomendável verificar se o arquivo existe antes de carregá-lo, porque você não deseja continuar executando seu script se um arquivo de configuração não estiver presente.

mbiber
fonte
0

Eu estou usando este ...

#!/bin/bash

CFG_FILE=/etc/test.conf
CFG_CONTENT=$(cat $CFG_FILE | sed -r '/[^=]+=[^=]+/!d' | sed -r 's/\s+=\s/=/g')
eval "$CFG_CONTENT"

O arquivo conf é analisado com sed e depois avaliado como atribuições variáveis ​​simples

Primeiro sed analisando valores-chave (também suporta = cercado por espaços)

O segundo sed remove espaços ao redor = sinal para atribuição variável válida

Pode ser adicionado outro tratamento sed

Todos os outros textos não correspondentes no arquivo conf serão removidos (incluindo # ou; comentário e outros)

Esteja ciente de que os comandos do shell de linha única também podem ser avaliados neste arquivo de configuração!

smARTin
fonte
1
Uso inútil decat . Por que você invoca sedduas vezes consecutivas? Você pode encadear sedcomandos da seguinte maneira:sed -r '/[^=]+=[^=]+/!d;s/\s+=\s/=/g'
David Foerster
Além disso, este sedcomando gerará uma saída vazia para as entradas do arquivo de configuração de amostra na pergunta. -1
David Foerster
1
Outra possível melhoria: use o .redirecionamento de comando e processo, em vez de variáveis ​​temporárias, para manter a saída do subprocesso:. <(sed ... "$CFG_FILE")
David Foerster
Obviamente, meu exemplo usa key = value como arquivo de configuração. É uma prática melhor que "valor-chave". Usando key = value, meu exemplo funciona. Seu encadeamento de comandos sed é melhor, admito, mas o comando source espera o arquivo para que possamos usar:, o eval $(sed -r '/[^=]+=[^=]+/!d;s/\s+=\s/=/g' "$CFG_FILE")que é muito melhor para entender o que está acontecendo.
smARTin
Sim, .espera que um arquivo e a substituição do processo sejam expandidos para um arquivo. . <(echo echo foo)funciona da maneira esperada no Bash v4.3.11. Em pequenos exemplos, provavelmente não importa. Eu preferiria não passar vários kilobytes através da substituição de comandos.
David Foerster 27/02
0
#------------------------------------------------------------------------------
# parse the ini like $0.$host_name.cnf and set the variables
# cleans the unneeded during after run-time stuff. Note the MainSection
# courtesy of : http://mark.aufflick.com/blog/2007/11/08/parsing-ini-files-with-sed
#------------------------------------------------------------------------------
doParseConfFile(){
    # set a default cnfiguration file
    cnf_file="$run_unit_bash_dir/$run_unit.cnf"

    # however if there is a host dependant cnf file override it
    test -f "$run_unit_bash_dir/$run_unit.$host_name.cnf" \
    && cnf_file="$run_unit_bash_dir/$run_unit.$host_name.cnf"

    # yet finally override if passed as argument to this function
    # if the the ini file is not passed define the default host independant ini file
    test -z "$1" || cnf_file=$1;shift 1;


    test -z "$2" || ini_section=$2;shift 1;
    doLog "DEBUG read configuration file : $cnf_file"
    doLog "INFO read [$ini_section] section from config file"

    # debug echo "@doParseConfFile cnf_file:: $cnf_file"
    # coud be later on parametrized ...
    test -z "$ini_section" && ini_section='MAIN_SETTINGS'

    doLog "DEBUG reading: the following configuration file"
    doLog "DEBUG ""$cnf_file"
    ( set -o posix ; set ) | sort >"$tmp_dir/vars.before"

    eval `sed -e 's/[[:space:]]*\=[[:space:]]*/=/g' \
        -e 's/#.*$//' \
        -e 's/[[:space:]]*$//' \
        -e 's/^[[:space:]]*//' \
        -e "s/^\(.*\)=\([^\"']*\)$/\1=\"\2\"/" \
        < $cnf_file \
        | sed -n -e "/^\[$ini_section\]/,/^\s*\[/{/^[^#].*\=.*/p;}"`

    ( set -o posix ; set ) | sort >"$tmp_dir/vars.after"

    doLog "INFO added the following vars from section: [$ini_section]"
    cmd="$(comm -3 $tmp_dir/vars.before $tmp_dir/vars.after | perl -ne 's#\s+##g;print "\n $_ "' )"
    echo -e "$cmd"
    echo -e "$cmd" >> $log_file
    echo -e "\n\n"
    sleep 1; printf "\033[2J";printf "\033[0;0H" # and clear the screen
}
#eof func doParseConfFile
Yordan Georgiev
fonte