Jeg har brug for at oprette en config-fil til mit eget script:
Her er et eksempel:
script:
#!/bin/bash source /home/myuser/test/config echo "Name=$nam" >&2 echo "Surname=$sur" >&2
Indhold i /home/myuser/test/config
:
nam="Mark" sur="Brown"
det fungerer!
Mit spørgsmål: er dette den rigtige måde at gør dette eller der “på andre måder?
Kommentarer
- Variablerne skal være øverst. I ‘ m overrasket over, at det fungerer. Hvor som helst, hvorfor har du brug for en konfigurationsfil? Planlægger du at bruge disse variabler et andet sted?
- Faheem, jeg har brug for variablerne, fordi mit script har mange muligheder: at bruge en konfigurationsfil vil forenkle scriptet. Tak
- IMHO det er fint. Jeg ville gøre på denne måde.
-
abcde
gør det også på denne måde og det er et ret stort program (for et shell-script). Du kan se det her .
Svar
source
er ikke sikkert, da det udfører vilkårlig kode. Dette er muligvis ikke et problem for dig, men hvis filtilladelser er forkerte, kan det være muligt for en hacker med filsystemadgang at udføre kode som en privilegeret bruger ved at injicere kode i en konfigurationsfil, der er indlæst med et ellers sikret script, såsom et init script.
Indtil videre er den bedste løsning, jeg har været i stand til at identificere, den klodset genopfinde-hjulet-løsning:
myscript.conf
password=bar echo rm -rf / PROMPT_COMMAND="echo "Sending your last command $(history 1) to my email"" hostname=localhost; echo rm -rf /
Brug af source
, dette vil køre echo rm -rf /
to gange samt ændre den kørende bruger “s $PROMPT_COMMAND
. I stedet skal du gøre 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
Svar venligst, hvis du finder en sikkerhedsudnyttelse i min kode.
Kommentarer
Svar
Analyser konfigurationsfilen, udfør den ikke.
Jeg skriver i øjeblikket en applikation på arbejdspladsen, der bruger en ekstrem simpel XML-konfiguration:
<config> <username>username-or-email</username> <password>the-password</password> </config>
I shell-scriptet (“applikationen”) er det hvad jeg gør for at få ved brugernavnet (mere eller mere mindre, jeg har lagt det i en shell-funktion):
username="$( xml sel -t -v "/config/username" "$config_file" )"
xml
kommandoen er XMLStarlet , som er tilgængelig for de fleste enheder.
Jeg bruger XML, da andre dele af applikationen også beskæftiger sig med data kodet i XML-filer, så det var nemmest.
Hvis du foretrækker JSON, er der “s jq
hvilket er en let at bruge shell JSON parser.
Min konfigurationsfil ser sådan ud i JS ON:
{ "username": "username-or-email", "password": "the-password" }
Og så ville jeg få brugernavnet i scriptet:
username="$( jq -r ".username" "$config_file" )"
Kommentarer
- At udføre scriptet har en række fordele og ulemper. De største ulemper er sikkerhed, hvis nogen kan ændre konfigurationsfilen, kan de udføre kode, og det er sværere at gøre det idiot-bevis. Fordelene er hastighed, på en simpel test er det mere end 10.000 gange hurtigere at hente en konfigurationsfil end at køre pq, og fleksibilitet, enhver, der kan lide ape-patch-python, vil sætte pris på dette.
- @icarus Hvor stor konfigurationsfiler støder du normalt på, og hvor ofte har du brug for at analysere dem i en session? Bemærk også, at der kan være flere værdier ud af XML eller JSON på én gang.
- Normalt kun få (1 til 3) værdier. Hvis du bruger
eval
til at indstille flere værdier, udfører du valgte dele af konfigurationsfilen :-). - @icarus Jeg tænkte arrays … Ingen grund til at
eval
noget. Performance-hit for at bruge et standardformat med en eksisterende parser (selvom det ‘ er en ekstern hjælpeprogram) er ubetydelig i forhold til robustheden, mængden af kode, brugervenligheden og vedligeholdelsesevne. - +1 for ” parse konfigurationsfilen, don ‘ t udfør den ”
Svar
Her er en ren og bærbar version, der er kompatibel med Bash 3 og op, både på Mac og Linux.
Det specificerer alle standardindstillinger i en separat fil for at undgå behovet for en enorm, rodet, duplikeret “standard” -konfigurationsfunktion i alle dine shell-scripts. Og det giver dig mulighed for at vælge mellem at læse med eller uden standardnedfald:
config.cfg :
myvar=Hello World
config.cfg.defaults :
myvar=Default Value othervar=Another Variable
config.shlib (dette er et bibliotek, så der 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 scripts, hvor du vil læs konfigurationsværdier) :
#!/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 af testscriptet:
- Bemærk, at alle anvendelser af config_get i test.sh er pakket med dobbelt anførselstegn. Ved at indpakke hvert config_get i dobbelt anførselstegn, sikrer vi, at tekst i variabelværdien aldrig fejlagtigt fortolkes som flag. Og det sikrer, at vi bevarer det hvide rum korrekt, f.eks. Flere mellemrum i træk i konfigurationsværdien.
- Og hvad er det
printf
-linjen? Nå, det “noget, du skal være opmærksom på:echo
er en dårlig kommando til udskrivning af tekst, som du ikke har kontrol over. Selv hvis du bruger dobbelt anførselstegn, fortolker det flag. Prøv at indstillemyvar
(iconfig.cfg
) til-e
, og du vil se en tom linje, fordiecho
vil tro, at det er et flag. Menprintf
har ikke det problem.printf --
siger “udskriv dette, og fortolk ikke noget som flag”, og"%s\n"
siger “formatere output som en streng med en efterfølgende ny linje, og endelig er den sidste parameter værdien for printf at formatere. - Hvis du ikke vil ekko værdier til skærmen, skal du blot tildele dem normalt, som
myvar="$(config_get myvar)";
. Hvis du vil udskrive dem på skærmen, foreslår jeg at bruge printf for at være helt sikker mod alle ekko-inkompatible strenge, der måtte være i brugerkonfigurationen. Men ekko er fint, hvis den bruger-leverede variabel ikke er det første tegn i strengen, du gentager, da det er den eneste situation, hvor “flag” kunne fortolkes, så noget somecho "foo: $(config_get myvar)";
er sikkert, da ” foo “begynder ikke med et bindestreg og fortæller derfor ekko, at resten af strengen heller ikke markerer for det. 🙂
Kommentarer
- @ user2993656 Tak, fordi du opdagede, at min oprindelige kode stadig havde mit private konfigurationsfilnavn (miljø.cfg) i stedet for det rigtige. Hvad angår ” echo -n ” redigering, du gjorde, det afhænger af den anvendte shell. På Mac / Linux Bash, ” echo -n ” betyder ” ekko uden efterfølgende ny linje “, hvilket jeg gjorde for at undgå at følge nye linjer. Men det ser ud til at arbejde nøjagtigt det samme uden det, så tak for redigeringerne!
- Faktisk gik jeg bare igennem og omskrev det til at bruge printf i stedet for ekko, hvilket sikrer, at vi ‘ slipper risikoen for, at ekko fejler ” flag ” i konfigurationsværdierne.
- Jeg kan virkelig godt lide denne version. Jeg faldt
config.cfg.defaults
i stedet for at definere dem på tidspunktet for opkald til$(config_get var_name "default_value")
. tritarget.org/static/… - Ligeledes – dette er fantastisk.
Svar
Den mest almindelige, effektive og korrekte måde er at bruge source
eller .
som en stenografisk form. For eksempel:
source /home/myuser/test/config
eller
. /home/myuser/test/config
Noget at overveje er dog sikkerhedsproblemer, som brug af en ekstra eksternt konfigurationsfil kan rejse, forudsat at yderligere kode kan indsættes. For mere information, herunder hvordan man finder og løser dette problem, vil jeg anbefale at se på afsnittet “Sikker det” i http://wiki.bash-hackers.org/howto/conffile#secure_it
Kommentarer
- Jeg havde store forhåbninger til den artikel (kom også i mine søgeresultater), men forfatteren ‘ s forslag om at forsøge at bruge regex til at filtrere ondsindet kode er en øvelse i nytteløshed.
- Proceduren med punkt, kræver en absolut sti?Med den relative fungerer det ikke ‘ t
Svar
Jeg bruger dette i mine scripts:
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 understøtte alle tegnkombinationer, undtagen nøgler kan ikke have =
i dem, da det er separatoren. Alt andet 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 sikkert, da det ikke bruger source
eller eval
.
Kommentarer
- Er det ikke ‘ t dette antages for først at oprette konfigurationsfilen, hvis den ikke findes ‘ t? Den ‘ t, men
touch -a "${path}"
vil selvfølgelig sikre, at den eksisterer, også uden at frivoløst opdatere sin mtime.
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
sikrer, at du kun får variablerne fra common.vars
Opdatering: En illustration af sikkerheden er, at
env -i "touch evil1 foo=omg boo=$(touch evil2)"
ikke producerer nogen berørte filer. Testet på mac med bash, dvs. ved hjælp af bsd env.
Kommentarer
- Se bare hvordan evil1 og evil2 filer oprettes, hvis du sætter dette til komm on.vars “ `touch touch1 foo = omg boo = $ (touch evil2)` `
- @pihentagy For mig producerer følgende ingen berørte filer
env -i 'touch evil1 foo=omg boo=$(touch evil2)'
. Kører på mac. - faktisk, men har ikke adgang til foo. Jeg ‘ har prøvet
env -i ... myscript.sh
og inde i dette script er foo ikke defineret. Men hvis du fjerner ” affald “, fungerer det. Så tak for at forklare. : +1:
Svar
De fleste brugere, selvom ikke mange containere, har allerede git
binær. Brug derfor ikke git config
til applikationskonfigurationsstyring ved hjælp af en dedikeret ikke-modstridende konfigurationsfil som i eksemplerne 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 yderligere kommandoer, se dens mand side. Det er klogt først at sikre, at konfigurationsfilen findes:
touch -a ~/.myapp
Svar
Denne virker sikker og kort. Du er velkommen til at knække dette hensynsløst. Jeg vil gerne vide om en bedre måde.
TL; DR;
while read LINE; do declare "$LINE"; done < evil.conf
Jeg bruger bash 4.3.48.
Det er også i overensstemmelse med bash --posix
. Se bunden for test.
Men sh
understøtter det ikke på grund af declare
.
Grundlæggende test for dem, der ønsker bevis
Opret 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"
Indlæs konfigurationen med uddraget
while read LINE; do declare "$LINE"; done < evil.conf
Output (se desinficeringsmiddel i aktion)
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
Lad os kontrollere værdier nu
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"
Kontroller bivirkninger (ingen bivirkninger):
ls evil evil2
ls: cannot access "evil": No such file or directory ls: cannot access "evil2": No such file or directory
Tillæg. 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 mit scenario, source
eller .
w lige så fint, men jeg ville støtte lokale miljøvariabler (dvs. FOO=bar myscript.sh
) med forrang over konfigurerede variabler. Jeg ønskede også, at konfigurationsfilen skulle redigeres af brugeren og være behagelig for nogen, der plejede at hente konfigurationsfiler, og holde den så lille / enkel som muligt for ikke at distrahere fra hovedfokuset i mit meget lille script.
Dette er hvad 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 væsentlige – det kontrollerer for variable definitioner (uden at være meget fleksibel med hvidt mellemrum) og omskriver disse linjer, så værdi konverteres til en standard for den variabel, og variablen ændres ikke, hvis den findes, ligesom XDG_CONFIG_HOME
variablen ovenfor. Den kilder denne ændrede version af konfigurationsfilen og fortsætter.
Fremtidigt arbejde kan gøre sed
scriptet mere robust, filtrere linjer, der ser underlige ud eller ikke er ” t definitioner osv., ikke bryde i slutningen af linjekommentarer – men det er godt nok for mig indtil videre.
Svar
Du kan gøre det:
#!/bin/bash name="mohsen" age=35 cat > /home/myuser/test/config << EOF Name=$name Age=$age EOF
*
-indgange korrekt, men så hvad i Bash håndterer den karakter godt?my\\password