Najlepszy sposób na odczytanie pliku konfiguracyjnego w bash

23

Jaki jest najlepszy sposób na odczytanie pliku konfiguracyjnego w bash?
Na przykład masz skrypt i nie chcesz ręcznie wypełniać całej konfiguracji za każdym razem, gdy wywołujesz skrypt.

Edycja 1: Myślę, że nie wyjaśniłem tego, więc: chcę tylko ... Mam plik konfiguracyjny, który jest podobny

variable_name value  
variable_name value ...  

i chcę to przeczytać. Wiem, że mogłem po prostu rzucić okiem na argumenty, których szukam ... ale może jest bardziej inteligentny sposób :)

Lodowaty
źródło
2
Musisz pokazać, jaką konfigurację masz na myśli. Podaj przykład.
muru
Zobacz także bardzo podobne pytanie dotyczące Unix i Linux StackExchange.
Michael

Odpowiedzi:

38

Jak powiedział mbiber, sourcekolejny plik. Na przykład plik konfiguracyjny (powiedzmy some.config) to:

var1=val1
var2=val2

Twój skrypt może wyglądać następująco:

#! /bin/bash

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

. /path/to/some.config

echo "$var1" "$var2"

Wiele plików /etc/defaultzwykle służy jako pliki konfiguracyjne dla innych skryptów powłoki w podobny sposób. Bardzo częstym przykładem zamieszczonych tutaj postów jest /etc/default/grub. Ten plik służy do ustawiania opcji konfiguracji GRUB-a, ponieważ grub-mkconfigjest to skrypt powłoki, który go pozyskuje:

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

Jeśli naprawdę musisz przetworzyć konfigurację formularza:

var1 some value 1
var2 some value 2

Następnie możesz zrobić coś takiego:

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

(Możesz także zrobić coś takiego eval "$var=$value", ale jest to bardziej ryzykowne niż pozyskiwanie skryptu. Możesz nieumyślnie złamać to łatwiej niż plik źródłowy.)

muru
źródło
To świetny sposób, mogłem użyć sed zastępującego białe znaki dla „=”
IcyIcyIce
1
@IcyIcyIce Po prostu nie. Dlaczego nie możesz tego pisać =od samego początku?
muru
1
@IcyIcyIce Tak właśnie powinno być. Możesz używać kodu powłoki w różnych plikach w /etc/default- i głównie do dobrego użytku. Jeśli twój nieświadomy użytkownik ma dostęp do konfiguracji dla czegoś działającego jako root, jest to już utracona przyczyna.
muru
1
@IcyIcyIce poproś nauczycieli, aby wyjaśnili, czego chcą. Częścią nauki programowania jest jasne poznanie wymagań.
Muru
1
@IcyIcyIce zobacz aktualizację.
muru
4

Oczywiście nie jestem bashtutaj specjalistą, ale koncepcja nie powinna być inna w żadnym używanym języku:

Przykład

W poniższym przykładzie możesz użyć (bardzo) podstawowego skryptu, aby ustawić ciąg lub wydrukować ciąg, zgodnie z ustawieniem w pliku konfiguracyjnym:

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

Następnie

  • Aby ustawić ciąg w pliku konfiguracyjnym:

    $ '/home/jacob/Bureaublad/test.sh' set "Een aap op een fiets, hoe vind je zoiets?"
  • Następnie, aby wydrukować ciąg zdefiniowany w „pliku konfiguracyjnym”:

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

Oczywiście w prawdziwym skrypcie stosowanym musisz dodać wiele rzeczy, aby upewnić się, że argumenty są poprawne, zdecydować, co zrobić, gdy dane wejściowe są nieprawidłowe, plik ustawień nie istnieje itp.

To jest podstawowy pomysł

Jacob Vlijm
źródło
dzięki, właśnie aktualizuję pytanie, które chyba nie wyjaśniłem ... ale w prostych przypadkach zadziałałoby to jak urok
IcyIcyIce
4

Użyj sourcelub, .aby załadować plik.

source /path/to/file

lub

. /path/to/file

Zaleca się również sprawdzenie, czy plik istnieje przed załadowaniem go, ponieważ nie chcesz kontynuować uruchamiania skryptu, jeśli plik konfiguracyjny nie jest obecny.

mbiber
źródło
0

Używam tego ...

#!/bin/bash

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

Plik Conf jest analizowany z sed, a następnie oceniany jako proste przypisanie zmiennej

Najpierw analizowane wartości klucza sed (obsługuje także = otoczony spacjami)

Drugi sed usuwa spacje wokół = znak dla prawidłowego przypisania zmiennej

Można dodać dalsze leczenie

Wszystkie inne niepasujące teksty w pliku conf zostaną usunięte (w tym # lub; komentarz i inne)

Należy pamiętać, że polecenia powłoki jednowierszowej można również oceniać z tego pliku konfiguracyjnego!

smARTin
źródło
1
Bezużyteczne użyciecat . Dlaczego odwołujesz się seddwa razy jeden po drugim? Możesz połączyć sedpolecenia w następujący sposób:sed -r '/[^=]+=[^=]+/!d;s/\s+=\s/=/g'
David Foerster
Również to sedpolecenie wygeneruje puste dane wyjściowe dla przykładowych wpisów pliku konfiguracyjnego w pytaniu. -1
David Foerster,
1
Kolejna możliwa poprawa: użyj .przekierowania polecenia i procesu zamiast zmiennych tymczasowych, aby zachować wyniki podprocesu:. <(sed ... "$CFG_FILE")
David Foerster
Oczywiście mój przykład używa key = wartość jak plik konfiguracyjny. Jest to lepsza praktyka niż „kluczowa wartość”. Za pomocą klucza = wartość działa mój przykład. Przyznaję, że twoje łączenie komend sed jest lepsze, ale komenda source oczekuje pliku, abyśmy mogli użyć:, eval $(sed -r '/[^=]+=[^=]+/!d;s/\s+=\s/=/g' "$CFG_FILE")co jest o wiele lepsze, aby zrozumieć, co się dzieje.
smARTin
Tak, .oczekuje pliku, a podstawienie procesu zostanie rozszerzone do pliku. . <(echo echo foo)działa tak, jak oczekiwano w Bash v4.3.11. W małych przykładach prawdopodobnie nie będzie to miało znaczenia. Wolałbym jednak nie przekazywać wielu kilobajtów przez podstawianie poleceń.
David Foerster
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
źródło