Jag måste skapa en konfigurationsfil för mitt eget skript:
Här är ett exempel:
skript:
#!/bin/bash source /home/myuser/test/config echo "Name=$nam" >&2 echo "Surname=$sur" >&2
Innehåll i /home/myuser/test/config
:
nam="Mark" sur="Brown"
som fungerar!
Min fråga: är detta rätt sätt att gör detta eller där ”på andra sätt?
Kommentarer
- Variablerna ska vara högst upp. Jag ’ m förvånad över att det fungerar. Hur som helst, varför behöver du en konfigurationsfil? Planerar du att använda dessa variabler någon annanstans?
- Faheem, jag behöver variablerna eftersom mitt skript har många alternativ: att använda en config-filen kommer att förenkla skriptet. Tack
- IMHO det är bra. Jag skulle göra så här.
-
abcde
gör det också på detta sätt och det är ett ganska stort program (för ett skalskript). Du kan titta på det här .
Svar
source
är inte säkert eftersom det kommer att köra godtycklig kod. Det här kanske inte är ett problem för dig, men om filbehörigheterna är felaktiga kan det vara möjligt för en angripare med filsystemåtkomst att köra kod som en privilegierad användare genom att injicera kod i en konfigurationsfil som laddas med ett annars säkert skript, t.ex. init-skript.
Hittills är den bästa lösningen jag har kunnat identifiera den klumpiga återuppfinning-ratten-lö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 /
Med source
, detta skulle köra echo rm -rf /
två gånger, samt ändra den löpande användaren ”s $PROMPT_COMMAND
. Istället gör du detta:
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
Svara om du hittar en säkerhetsutnyttjande i min kod.
Kommentarer
Svar
Analysera konfigurationsfilen, kör inte den.
Jag skriver för närvarande en applikation på jobbet som använder en extremt enkel XML-konfiguration:
<config> <username>username-or-email</username> <password>the-password</password> </config>
I skalskriptet (”applikationen”) är det här jag gör för att få användarnamnet (mer eller mer mindre, jag har lagt den i en skalfunktion):
username="$( xml sel -t -v "/config/username" "$config_file" )"
xml
-kommandot är XMLStarlet , som är tillgängligt för de flesta enheter.
Jag använder XML eftersom andra delar av applikationen också handlar om data som är kodade i XML-filer, så det var lättast.
Om du föredrar JSON finns ”s jq
vilket är ett lättanvänd skal-JSON-parser.
Min konfigurationsfil ser ungefär så ut i JS PÅ:
{ "username": "username-or-email", "password": "the-password" }
Och då skulle jag få användarnamnet i skriptet:
username="$( jq -r ".username" "$config_file" )"
Kommentarer
- Att utföra skriptet har ett antal fördelar och nackdelar. De största nackdelarna är säkerhet, om någon kan ändra konfigurationsfilen kan de köra kod och det är svårare att göra det idiot bevis. Fördelarna är hastighet, på ett enkelt test är det mer än 10 000 gånger snabbare att hämta en konfigurationsfil än att köra pq och flexibilitet, alla som gillar apa-lapp-python kommer att uppskatta detta. konfigurationsfiler stöter du vanligtvis på och hur ofta behöver du analysera dem i en session? Lägg också märke till att flera värden kan tas från XML eller JSON på en gång.
- Vanligtvis bara ett fåtal (1 till 3) värden. Om du använder
eval
för att ställa in flera värden så kör du valda delar av konfigurationsfilen :-). - @icarus Jag tänkte arrays … Inget behov av att
eval
någonting. Prestandahittet med att använda ett standardformat med en befintlig parser (även om det ’ är ett externt verktyg) är försumbar jämfört med robustheten, mängden kod, användarvänligheten och underhållsförmåga. - +1 för ” analysera konfigurationsfilen, gör ’ t kör den ”
Svar
Här är en ren och bärbar version som är kompatibel med Bash 3 och upp, både på Mac och Linux.
Den specificerar alla standardvärden i en separat fil för att undvika behovet av en enorm, rörig, duplicerad ”standard” -konfigurationsfunktion i alla dina skalskript. Och det låter dig välja mellan att läsa med eller utan standardfall:
config.cfg :
myvar=Hello World
config.cfg.defaults :
myvar=Default Value othervar=Another Variable
config.shlib (detta är ett bibliotek, så det finns 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 några skript där du vill läs konfigurationsvärden) :
#!/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
Förklaring av testskriptet:
- Observera att alla användningar av config_get i test.sh är inslagna i dubbla citat. Genom att sätta in alla config_get i dubbla citat ser vi till att texten i variabelvärdet aldrig tolkas felaktigt som flaggor. Och det säkerställer att vi bevarar det vita utrymmet ordentligt, till exempel flera mellanslag i rad i konfigurationsvärdet.
- Och vad är det för
printf
? ”något du bör vara medveten om:echo
är ett dåligt kommando för att skriva ut text som du inte har kontroll över. Även om du använder dubbla citat tolkar det flaggor. Försök att ställa inmyvar
(iconfig.cfg
) till-e
så visas en tom rad, eftersomecho
tror att det är en flagga. Menprintf
har inte det problemet.printf --
säger ”skriv ut detta och tolk inte något som flaggor” och"%s\n"
säger ”formatera utdata som en sträng med en efterföljande ny rad, och slutligen är den sista parametern värdet för printf att formatera. - Om du inte kommer att upprepa värden på skärmen, skulle du helt enkelt tilldela dem normalt, som
myvar="$(config_get myvar)";
. Om du ska skriva ut dem på skärmen föreslår jag att du använder printf för att vara helt säker mot alla ekokompatibla strängar som kan finnas i användarkonfigurationen. Men eko är bra om den användarinställda variabeln inte är det första tecknet i strängen du upprepar, eftersom det är den enda situationen där ”flaggor” kan tolkas, så något somecho "foo: $(config_get myvar)";
är säkert, eftersom ” foo ”börjar inte med en streck och säger därför till eko att resten av strängen inte heller flaggar för den. 🙂
Kommentarer
- @ user2993656 Tack för att du upptäckte att min ursprungliga kod fortfarande hade mitt privata konfigurationsfilnamn (miljö.cfg) istället för rätt. När det gäller ” echo -n ” redigera du gjorde, det beror på det använda skalet. På Mac / Linux Bash, ” echo -n ” betyder ” eko utan efterföljande ny linje ”, vilket jag gjorde för att undvika att följa nya rader. Men det verkar fungera exakt samma utan det, så tack för redigeringarna!
- Egentligen gick jag igenom och skrev om det för att använda printf istället för eko, vilket säkerställer att vi ’ kommer att bli av med risken för att ekotolkar ” flaggor ” i konfigurationsvärdena.
- Jag gillar verkligen den här versionen. Jag tappade
config.cfg.defaults
istället för att definiera dem när jag ringde$(config_get var_name "default_value")
. tritarget.org/static/… - Likaså – det här är fantastiskt.
Svar
Det vanligaste, effektivaste och korrekta sättet är att använda source
eller .
som en stenografisk form. Till exempel:
source /home/myuser/test/config
eller
. /home/myuser/test/config
Något att tänka på är dock säkerhetsproblem som kan användas med ytterligare en extern konfigurationsfil med utgångspunkt, med tanke på att ytterligare kod kan infogas. För mer information, inklusive hur du upptäcker och löser problemet, rekommenderar jag att du tittar på avsnittet ”Säkra det” i http://wiki.bash-hackers.org/howto/conffile#secure_it
Kommentarer
- Jag hade stora förhoppningar på den artikeln (kom också upp i mina sökresultat), men författaren ’ förslag på att försöka använda regex för att filtrera bort skadlig kod är en övning i meningslöshet.
- Proceduren med punkt, kräver en absolut sökväg?Med den relativa fungerar den inte ’ t
Svar
Jag använder detta i mina skript:
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 }
Ska stödja alla teckenkombinationer, utom tangenterna kan inte ha =
i dem, eftersom det är separatorn. Allt annat fungerar.
% 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
Detta är också helt säkert eftersom det inte använder source
eller eval
.
Kommentarer
- Är inte ’ t detta antas att först skapa konfigurationsfilen om den inte finns ’ t? Den finns inte ’ t men
touch -a "${path}"
kommer naturligtvis att säkerställa att den existerar, även utan att frivolikt uppdatera sin mtime.
Svar
Detta är kortfattad och säker:
# 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äkerställer att du bara får variablerna från common.vars
Uppdatering: En säkerhetsillustration är att
env -i "touch evil1 foo=omg boo=$(touch evil2)"
Ger inga berörda filer. Testat på mac med bash, dvs med hjälp av bsd-env.
Kommentarer
- Se bara hur evil1 och evil2-filer skapas om du lägger detta till komm on.vars touch evil1 foo = omg boo = $ (touch evil2) ”
- @pihentagy För mig producerar följande inga berörda filer
env -i 'touch evil1 foo=omg boo=$(touch evil2)'
. Körs på mac. - verkligen, men kan inte komma åt foo. Jag ’ har försökt
env -i ... myscript.sh
och inuti det skriptet definieras inte foo. Om du tar bort ” sopor ” fungerar det dock. Så tack för att du förklarade. : +1:
Svar
De flesta användare, även om inte många containrar, har redan git
binärt. Varför inte därför använda git config
för applikationskonfigurationshantering med en dedikerad icke-motstridig konfigurationsfil som i exemplen nedan?
# 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
För ytterligare kommandon, se dess man sida. Det är klokt att först se till att konfigurationsfilen finns:
touch -a ~/.myapp
Svar
Den här verkar säker och kort. Känn dig fri att knäcka detta hänsynslöst. Jag skulle vilja veta om ett bättre sätt.
TL; DR;
while read LINE; do declare "$LINE"; done < evil.conf
Jag använder bash 4.3.48.
Det är också kompatibelt med bash --posix
. Se botten för test.
Men sh
stöder det inte på grund av declare
.
Grundläggande test för dem som vill ha bevis
Skapa 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"
Ladda konfigurationen med kodavsnittet
while read LINE; do declare "$LINE"; done < evil.conf
Output (se sanitizer 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
Låt oss kontrollera värdena 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"
Kontrollera biverkningar (inga biverkningar):
ls evil evil2
ls: cannot access "evil": No such file or directory ls: cannot access "evil2": No such file or directory
Bilaga. Testa 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
För mitt scenario, source
eller .
w lika bra, men jag ville stödja lokala miljövariabler (dvs. FOO=bar myscript.sh
) med företräde framför konfigurerade variabler. Jag ville också att konfigurationsfilen skulle kunna redigeras av användaren och vara bekväm för någon som använde sig av konfigurationsfiler och att den skulle vara så liten / enkel som möjligt, för att inte distrahera från huvudinriktningen i mitt mycket lilla skript.
Det här är vad jag 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 grund och botten – det söker efter variabla definitioner (utan att vara mycket flexibel när det gäller blanksteg) och skriver om dessa rader så att värdet konverteras till en standard för den variabeln, och variabeln är omodifierad om den hittas, som XDG_CONFIG_HOME
variabeln ovan. Den källor till den här ändrade versionen av konfigurationsfilen och fortsätter.
Framtida arbete kan göra sed
-skriptet mer robust, filtrera ut linjer som ser konstiga ut eller inte t definitioner, etc, inte bryta i slutet av radkommentarer – men det är tillräckligt bra för mig för tillfället.
Svar
Du kan göra det:
#!/bin/bash name="mohsen" age=35 cat > /home/myuser/test/config << EOF Name=$name Age=$age EOF
*
ingångar ordentligt, men sedan vad i Bash hanterar den karaktären bra?my\\password