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
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 å settemyvar
(iconfig.cfg
) til-e
, så ser du en tom linje, fordiecho
vil tro at det er et flagg. Menprintf
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 somecho "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
*
innganger riktig, men da hva i Bash håndterer denne karakteren godt?my\\password