Bruk konfigurasjonsfil for skallskriptet mitt

Jeg trenger å lage en konfigurasjonsfil for mitt eget skript:
Her er et eksempel:

skript:

#!/bin/bash source /home/myuser/test/config echo "Name=$nam" >&2 echo "Surname=$sur" >&2 

Innhold i /home/myuser/test/config:

nam="Mark" sur="Brown" 

som fungerer!

Spørsmålet mitt: er dette den riktige måten å gjør dette eller der «andre måter?

Kommentarer

  • Variablene skal være øverst. Jeg ‘ m overrasket over at det fungerer. Uansett, hvorfor trenger du en konfigurasjonsfil? Planlegger du å bruke disse variablene et annet sted?
  • Faheem, jeg trenger variablene fordi skriptet mitt har mange alternativer: å bruke en konfigurasjonsfil vil forenkle skriptet. Takk
  • IMHO det er greit. Jeg ville gjort på denne måten.
  • abcde gjør det også på denne måten og det er et ganske stort program (for et skallskript). Du kan se på det her .

Svar

source er ikke sikkert da det vil utføre vilkårlig kode. Dette kan ikke være et problem for deg, men hvis filtillatelsene er feil, kan det være mulig for en angriper med filsystemtilgang å utføre kode som en privilegert bruker ved å injisere kode i en konfigurasjonsfil lastet inn av et ellers sikret skript, for eksempel et init-skript.

Så langt er den beste løsningen jeg har kunnet identifisere, den klønete gjenoppfinning av rattløsningen:

myscript.conf

password=bar echo rm -rf / PROMPT_COMMAND="echo "Sending your last command $(history 1) to my email"" hostname=localhost; echo rm -rf / 

Bruk source, dette vil kjøre echo rm -rf / to ganger, samt endre den løpende brukeren «s $PROMPT_COMMAND. I stedet gjør du dette:

myscript.sh (Bash 4)

#!/bin/bash typeset -A config # init array config=( # set default values in config array [username]="root" [password]="" [hostname]="localhost" ) while read line do if echo $line | grep -F = &>/dev/null then varname=$(echo "$line" | cut -d "=" -f 1) config[$varname]=$(echo "$line" | cut -d "=" -f 2-) fi done < myscript.conf echo ${config[username]} # should be loaded from defaults echo ${config[password]} # should be loaded from config file echo ${config[hostname]} # includes the "injected" code, but it"s fine here echo ${config[PROMPT_COMMAND]} # also respects variables that you may not have # been looking for, but they"re sandboxed inside the $config array 

myscript.sh (Mac / Bash 3-kompatibel)

#!/bin/bash config() { val=$(grep -E "^$1=" myscript.conf 2>/dev/null || echo "$1=__DEFAULT__" | head -n 1 | cut -d "=" -f 2-) if [[ $val == __DEFAULT__ ]] then case $1 in username) echo -n "root" ;; password) echo -n "" ;; hostname) echo -n "localhost" ;; esac else echo -n $val fi } echo $(config username) # should be loaded from defaults echo $(config password) # should be loaded from config file echo $(config hostname) # includes the "injected" code, but it"s fine here echo $(config PROMPT_COMMAND) # also respects variables that you may not have # been looking for, but they"re sandboxed inside the $config array 

Vennligst svar hvis du finner en sikkerhetsutnyttelse i koden min.

Kommentarer

  • FYI dette er en Bash versjon 4.0-løsning som dessverre er underlagt vanvittige lisensieringsproblemer pålagt av Apple og ikke er tilgjengelig som standard på Mac
  • @Sukima Bra poeng. Jeg ‘ har lagt til en versjon som er kompatibel med Bash 3. Dens svakhet er at den ikke vil håndtere * innganger riktig, men da hva i Bash håndterer denne karakteren godt?
  • Det første skriptet mislykkes hvis passordet inneholder en tilbakeslag.
  • @Kusalananda Hva om tilbakeslag er unnslippe? my\\password
  • Denne versjonen tar flere sekunder å behandle konfigurasjonsfilen min, jeg fant løsningen fra gw0 for å være mye raskere.

Svar

Analyser konfigurasjonsfilen, ikke utfør den.

Jeg skriver for øyeblikket et program på jobben som bruker en ekstremt enkel XML-konfigurasjon:

<config> <username>username-or-email</username> <password>the-password</password> </config> 

I skallskriptet («applikasjonen») er det dette jeg gjør for å få ved brukernavnet (mer eller mindre, jeg har satt den i en skallfunksjon):

username="$( xml sel -t -v "/config/username" "$config_file" )" 

xml -kommandoen er XMLStarlet , som er tilgjengelig for de fleste enheter.

Jeg bruker XML siden andre deler av applikasjonen også håndterer data kodet i XML-filer, så det var enklest.

Hvis du foretrekker JSON, er det «s jq som er et enkel å bruke skall JSON-parser.

Konfigurasjonsfilen min vil se omtrent slik ut i JS PÅ:

{ "username": "username-or-email", "password": "the-password" } 

Og så skulle jeg få brukernavnet i skriptet:

username="$( jq -r ".username" "$config_file" )" 

Kommentarer

  • Å utføre skriptet har en rekke fordeler og ulemper. De viktigste ulempene er sikkerhet. Hvis noen kan endre konfigurasjonsfilen, kan de utføre kode, og det er vanskeligere å gjøre den idiotbestandig. Fordelene er hastighet, på en enkel test er det mer enn 10 000 ganger raskere å hente en konfigurasjonsfil enn å kjøre pq, og fleksibilitet, alle som liker ape-patch-python vil sette pris på dette. konfigurasjonsfiler møter du vanligvis på, og hvor ofte trenger du å analysere dem i en økt? Legg også merke til at flere verdier kan være ute av XML eller JSON på en gang.
  • Vanligvis bare noen få (1 til 3) verdier. Hvis du bruker eval til å angi flere verdier, kjører du utvalgte deler av konfigurasjonsfilen :-).
  • @icarus Jeg tenkte arrays … Ingen grunn til å eval noe. Ytelseshittet med å bruke et standardformat med en eksisterende parser (selv om det ‘ er et eksternt verktøy) er ubetydelig i forhold til robustheten, mengden kode, brukervennligheten og vedlikeholdsevne.
  • +1 for » analyser konfigurasjonsfilen, ikke ‘ t utfør den »

Svar

Her er en ren og bærbar versjon som er kompatibel med Bash 3 og opp, både Mac og Linux.

Den spesifiserer alle standardinnstillinger i en egen fil, for å unngå behovet for en enorm, rotete, duplisert «standard» -konfigurasjonsfunksjon i alle skallskriptene dine. Og det lar deg velge mellom å lese med eller uten tilbakefall:

config.cfg :

myvar=Hello World 

config.cfg.defaults :

myvar=Default Value othervar=Another Variable 

config.shlib (dette er et bibliotek, så det er ingen shebang-linje):

config_read_file() { (grep -E "^${2}=" -m 1 "${1}" 2>/dev/null || echo "VAR=__UNDEFINED__") | head -n 1 | cut -d "=" -f 2-; } config_get() { val="$(config_read_file config.cfg "${1}")"; if [ "${val}" = "__UNDEFINED__" ]; then val="$(config_read_file config.cfg.defaults "${1}")"; fi printf -- "%s" "${val}"; } 

test.sh (eller andre skript der du vil les konfigurasjonsverdier) :

#!/usr/bin/env bash source config.shlib; # load the config library functions echo "$(config_get myvar)"; # will be found in user-cfg printf -- "%s\n" "$(config_get myvar)"; # safer way of echoing! myvar="$(config_get myvar)"; # how to just read a value without echoing echo "$(config_get othervar)"; # will fall back to defaults echo "$(config_get bleh)"; # "__UNDEFINED__" since it isn"t set anywhere 

Forklaring av testskriptet:

  • Merk at alle bruk av config_get i test.sh er pakket inn i dobbelt anførselstegn. Ved å pakke hvert config_get inn i dobbelt anførselstegn, sørger vi for at tekst i variabelverdien aldri blir mistolket som flagg. Og det sikrer at vi opprettholder hvitt mellomrom riktig, for eksempel flere mellomrom på rad i konfigurasjonsverdien.
  • Og hva er det printf -linjen? Vel, det «noe du bør være oppmerksom på: echo er en dårlig kommando for å skrive ut tekst som du ikke har kontroll over. Selv om du bruker doble anførselstegn, vil det tolke flagg. Prøv å sette myvar (i config.cfg) til -e, så ser du en tom linje, fordi echo vil tro at det er et flagg. Men printf har ikke det problemet. printf -- sier «skriv ut dette, og ikke tolk noe som flagg», og "%s\n" sier «format utgangen som en streng med en etterfølgende ny linje, og til slutt er den siste parameteren verdien for printf å formatere.
  • Hvis du ikke kommer til å ekko verdier til skjermen, ville du bare tilordne dem normalt, som myvar="$(config_get myvar)";. Hvis du skal skrive dem ut på skjermen, foreslår jeg at du bruker printf for å være helt trygg mot eventuelle ekko-inkompatible strenger som kan være i brukerkonfigurasjonen. Men ekko er greit hvis den brukerstilførte variabelen ikke er det første tegnet i strengen du ekko, siden det er den eneste situasjonen der «flagg» kan tolkes, så noe sånt som echo "foo: $(config_get myvar)"; er trygt, siden » foo «begynner ikke med en bindestrek og forteller derfor ekko at resten av strengen ikke flagger for den heller. 🙂

Kommentarer

  • @ user2993656 Takk for at du oppdaget at den opprinnelige koden min fremdeles hadde mitt private konfigurasjonsfilnavn (environment.cfg) i stedet for den riktige. Når det gjelder » echo -n » redigere du gjorde, det avhenger av skallet som brukes. På Mac / Linux Bash, » echo -n » betyr » ekko uten etterfølgende ny linje «, som jeg gjorde for å unngå å følge nye linjer. Men det ser ut til å fungere nøyaktig det samme uten det, så takk for endringene!
  • Egentlig gikk jeg bare igjennom og skrev det om for å bruke printf i stedet for ekko, som sørger for at vi ‘ Jeg blir kvitt risikoen for at ekko tolker » flagg » i konfigurasjonsverdiene.
  • Jeg liker denne versjonen. Jeg droppet config.cfg.defaults i stedet for å definere dem når jeg ringte $(config_get var_name "default_value"). tritarget.org/static/…
  • På samme måte – dette er flott.

Svar

Den vanligste, mest effektive og korrekte måten er å bruke source, eller . som en stenografisk form. For eksempel:

source /home/myuser/test/config 

eller

. /home/myuser/test/config 

Noe å vurdere er imidlertid sikkerhetsproblemer som bruker en ekstra ekstern konfigurasjonsfil kan øke, gitt at tilleggskode kan settes inn. For mer informasjon, inkludert hvordan du oppdager og løser dette problemet, vil jeg anbefale å ta en titt på delen «Sikre det» i http://wiki.bash-hackers.org/howto/conffile#secure_it

Kommentarer

  • Jeg hadde store forhåpninger til den artikkelen (kom opp i søkeresultatene mine også), men forfatteren ‘ sitt forslag om å prøve å bruke regex for å filtrere ut ondsinnet kode er en øvelse i nytteløshet.
  • Fremgangsmåten med punktum, krever en absolutt bane?Med den relative fungerer det ikke ‘ t

Svar

Jeg bruker dette i skriptene mine:

sed_escape() { sed -e "s/[]\/$*.^[]/\\&/g" } cfg_write() { # path, key, value cfg_delete "$1" "$2" echo "$2=$3" >> "$1" } cfg_read() { # path, key -> value test -f "$1" && grep "^$(echo "$2" | sed_escape)=" "$1" | sed "s/^$(echo "$2" | sed_escape)=//" | tail -1 } cfg_delete() { # path, key test -f "$1" && sed -i "/^$(echo $2 | sed_escape).*$/d" "$1" } cfg_haskey() { # path, key test -f "$1" && grep "^$(echo "$2" | sed_escape)=" "$1" > /dev/null } 

Skal støtte alle tegnkombinasjoner, bortsett fra at tastene ikke kan ha = i dem, siden det er skilletegn. Alt annet fungerer.

% cfg_write test.conf mykey myvalue % cfg_read test.conf mykey myvalue % cfg_delete test.conf mykey % cfg_haskey test.conf mykey || echo "It"s not here anymore" It"s not here anymore 

Dette er også helt trygt siden det ikke bruker source eller eval.

Kommentarer

  • Er ikke ‘ t dette antas for å først opprette konfigurasjonsfilen hvis den ikke ‘ ikke eksisterer? Den ‘ t, men touch -a "${path}" vil selvsagt sørge for at den eksisterer, også uten å oppdatere mtime lettvint.

Svar

Dette er kortfattet og sikker:

# Read common vars from common.vars # the incantation here ensures (by env) that only key=value pairs are present # then declare-ing the result puts those vars in our environment declare $(env -i `cat common.vars`) 

-i sørger for at du bare får variablene fra common.vars

Oppdatering: En illustrasjon av sikkerheten er at

env -i "touch evil1 foo=omg boo=$(touch evil2)" 

Vil ikke produsere berørte filer. Testet på mac med bash, dvs. bruke bsd env.

Kommentarer

  • Bare se hvordan evil1 og evil2 filer blir opprettet hvis du setter dette til komm on.vars « touch evil1 foo = omg boo = $ (touch evil2) «
  • @pihentagy For meg produserer følgende ingen berørte filer env -i 'touch evil1 foo=omg boo=$(touch evil2)' . Kjører på mac.
  • faktisk, men har ikke tilgang til foo. Jeg ‘ har prøvd env -i ... myscript.sh og inne i skriptet er foo ikke definert. Hvis du fjerner » søppel «, vil det imidlertid fungere. Så takk for å forklare. : +1:

Svar

De fleste brukere, selv om det ikke er mange containere, har allerede git binær. Hvorfor ikke derfor bruke git config for programkonfigurasjonsadministrasjon ved hjelp av en dedikert ikke-motstridende konfigurasjonsfil som i eksemplene nedenfor?

# Set $ git config -f ~/.myapp core.mykey myval # Get $ git config -f ~/.myapp core.mykey myval # Get invalid $ git config -f ~/.myapp core.mykey $ echo $? 1 # List git config -f ~/.myapp -l core.mykey=myval # View $ cat ~/.myapp [core] mykey = myval 

For ytterligere kommandoer, se mannen sin side. Det er lurt å først sikre at konfigurasjonsfilen eksisterer:

touch -a ~/.myapp 

Svar

Denne virker trygg og kort. Du kan gjerne knekke dette nådeløst. Jeg vil vite om en bedre måte.

TL; DR;

while read LINE; do declare "$LINE"; done < evil.conf 

Jeg bruker bash 4.3.48.

Det er også i samsvar med bash --posix. Se bunnen for test.

Men sh støtter det ikke på grunn av declare.


Grunnleggende test for de som ønsker bevis

Opprett fil evil.conf

 echo > evil.conf " A=1 B=2 C=$(echo hello) # Could produce side-effect D=`touch evil` C=$((1+2)) E=$(ping 8.8.8.8 -n 3) echo hello # Could produce visible side-effect touch evil2 ping 8.8.8.8 -n 3 F=ok"  

Last konfigurasjonen med kodebiten

 while read LINE; do declare "$LINE"; done < evil.conf  

Utgang (se sanitizer i aksjon)

bash: declare: `": not a valid identifier bash: declare: `": not a valid identifier bash: declare: `# Could produce side-effect": not a valid identifier bash: declare: `echo hello": not a valid identifier bash: declare: `": not a valid identifier bash: declare: `# Could produce visible side-effect": not a valid identifier bash: declare: `touch evil2": not a valid identifier bash: declare: `ping 8.8.8.8 -n 3": not a valid identifier 

La oss sjekke verdiene nå

 for V in A B C D E F; do declare -p $V; done  
declare -- A="1" declare -- B="2" declare -- C="\$((1+2))" declare -- D="\`touch evil\`" declare -- E="\$(ping 8.8.8.8 -n 3)" declare -- F="ok" 

Sjekk bivirkninger (ingen bivirkninger):

 ls evil evil2  
ls: cannot access "evil": No such file or directory ls: cannot access "evil2": No such file or directory 

Vedlegg. Test bash --posix

bash -c "while read LINE; do declare "$LINE"; done < evil.conf; for V in A B C D E F; do declare -p $V; done" --posix 

Svar

For mitt scenario, source eller . w like greit, men jeg ønsket å støtte lokale miljøvariabler (dvs. FOO=bar myscript.sh) med forrang over konfigurerte variabler. Jeg ønsket også at konfigurasjonsfilen skulle kunne redigeres av brukeren og være behagelig for noen som pleide å hente konfigurasjonsfiler, og å holde den så liten / enkel som mulig, for ikke å distrahere fra hovedinnholdet i det veldig lille skriptet mitt.

Dette er hva jeg kom på:

CONF=${XDG_CONFIG_HOME:-~/config}/myscript.sh if [ ! -f $CONF ]; then cat > $CONF << CONF VAR1="default value" CONF fi . <(sed "s/^\([^=]\+\) *= *\(.*\)$/\1=${\1:-\2}/" < $CONF) 

I det vesentlige – det ser etter variable definisjoner (uten å være veldig fleksibel med hvitt mellomrom) og skriver om disse linjene slik at verdien konverteres til en standard for den variabelen, og variabelen er umodifisert hvis den blir funnet, som XDG_CONFIG_HOME -variabelen ovenfor. Den kilder denne endrede versjonen av konfigurasjonsfilen og fortsetter.

Fremtidig arbeid kan gjøre sed -skriptet mer robust, filtrere ut linjer som ser rare eller ikke ut » t definisjoner osv., ikke bryt på slutten av kommentarene til linjen – men dette er bra nok for meg for nå.

Svar

Du kan gjøre det:

#!/bin/bash name="mohsen" age=35 cat > /home/myuser/test/config << EOF Name=$name Age=$age EOF 

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *