Ik moet een configuratiebestand maken voor mijn eigen script:
Hier is een voorbeeld:
script:
#!/bin/bash source /home/myuser/test/config echo "Name=$nam" >&2 echo "Surname=$sur" >&2
Inhoud van /home/myuser/test/config
:
nam="Mark" sur="Brown"
dat werkt!
Mijn vraag: is dit de juiste manier om doe dit of zijn er “zijn andere manieren?
Reacties
- De variabelen moeten bovenaan staan. I ‘ m verbaasd dat het werkt. Hoe dan ook, waarom heb je een configuratiebestand nodig? Ben je van plan om deze variabelen ergens anders te gebruiken?
- Faheem, ik heb de variabelen nodig omdat mijn script veel opties heeft: een config-bestand zal het script semplificeren. Bedankt
- IMHO het is prima. Ik zou op deze manier doen.
-
abcde
doet het ook op deze manier en dat is een vrij groot programma (voor een shellscript). Je kunt het hier bekijken.
Answer
source
is niet veilig omdat het willekeurige code zal uitvoeren. Dit is misschien geen probleem voor u, maar als de bestandsrechten onjuist zijn, kan het voor een aanvaller met bestandssysteemtoegang mogelijk zijn om code uit te voeren als een geprivilegieerde gebruiker door code te injecteren in een configuratiebestand dat is geladen door een anderszins beveiligd script, zoals een init-script.
Tot dusverre is de beste oplossing die ik “heb kunnen identificeren de onhandige oplossing om het wiel opnieuw uit te vinden:
myscript.conf
password=bar echo rm -rf / PROMPT_COMMAND="echo "Sending your last command $(history 1) to my email"" hostname=localhost; echo rm -rf /
Met source
, dit zou echo rm -rf /
twee keer draaien, en ook de actieve gebruiker “s $PROMPT_COMMAND
veranderen. Doe in plaats daarvan dit:
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-compatibel)
#!/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
Reageer als je een beveiligingsprobleem in mijn code aantreft.
Opmerkingen
Answer
Parseer het configuratiebestand, voer het niet uit.
Ik schrijf momenteel een applicatie op het werk die gebruikt een uiterst eenvoudige XML-configuratie:
<config> <username>username-or-email</username> <password>the-password</password> </config>
In het shellscript (de “applicatie”) is dit wat ik doe om bij de gebruikersnaam te komen (meer of less, ik heb het in een shell-functie gestopt):
username="$( xml sel -t -v "/config/username" "$config_file" )"
Het xml
commando is XMLStarlet , die beschikbaar is voor de meeste Unices.
Ik gebruik XML omdat andere delen van de applicatie ook gegevens verwerken die in XML-bestanden zijn gecodeerd, dus het was het gemakkelijkst.
Als u de voorkeur geeft aan JSON, is er “s jq
wat een eenvoudig te gebruiken shell JSON-parser.
Mijn configuratiebestand zou er in JS ongeveer zo uitzien AAN:
{ "username": "username-or-email", "password": "the-password" }
En dan “zou ik de gebruikersnaam in het script krijgen:
username="$( jq -r ".username" "$config_file" )"
Opmerkingen
- Het uitvoeren van het script heeft een aantal voor- en nadelen. De belangrijkste nadelen zijn beveiliging: als iemand het configuratiebestand kan wijzigen, kan hij code uitvoeren en is het moeilijker om het idiootbestendig te maken. De voordelen zijn snelheid, op een eenvoudige test is het meer dan 10.000 keer sneller om een configuratiebestand te vinden dan om pq uit te voeren, en flexibiliteit, iedereen die van Monkey Patching Python houdt, zal dit waarderen.
- @icarus Hoe groot configuratiebestanden kom je gewoonlijk tegen, en hoe vaak moet je ze in één sessie ontleden? Merk ook op dat er in één keer meerdere waarden uit XML of JSON kunnen zijn gehaald.
- Gewoonlijk slechts een paar (1 tot 3) waarden. Als je
eval
gebruikt om meerdere waarden in te stellen, dan voer je geselecteerde delen van het configuratiebestand uit :-). - @icarus Ik dacht aan arrays … U hoeft niets
eval
te gebruiken. De prestatiehit van het gebruik van een standaardformaat met een bestaande parser (ook al is het ‘ een extern hulpprogramma) is verwaarloosbaar in vergelijking met de robuustheid, de hoeveelheid code, het gebruiksgemak en onderhoudbaarheid. - +1 voor ” ontleed het configuratiebestand, don ‘ om het uit te voeren ”
Answer
Hier is een schone en draagbare versie die compatibel is met Bash 3 en up, zowel op Mac als Linux.
Het specificeert alle standaardinstellingen in een apart bestand, om de noodzaak van een enorme, rommelige, gedupliceerde “defaults” -configuratiefunctie in al je shellscripts te vermijden. En het laat je kiezen tussen lezen met of zonder standaard fallbacks:
config.cfg :
myvar=Hello World
config.cfg.defaults :
myvar=Default Value othervar=Another Variable
config.shlib (dit is een bibliotheek, dus er is geen shebang-line):
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 (of scripts waar u lees configuratiewaarden) :
#!/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
Uitleg van het testscript:
- Merk op dat alle toepassingen van config_get in test.sh tussen dubbele aanhalingstekens staan. Door elke config_get tussen dubbele aanhalingstekens te plaatsen, zorgen we ervoor dat tekst in de variabele waarde nooit verkeerd wordt geïnterpreteerd als vlaggen. En het zorgt ervoor dat we witruimte correct behouden, zoals meerdere spaties achter elkaar in de configuratiewaarde.
- En wat is die regel
printf
? “is iets waar je op moet letten:echo
is een slecht commando voor het afdrukken van tekst waar je geen controle over hebt. Zelfs als u dubbele aanhalingstekens gebruikt, worden vlaggen geïnterpreteerd. Stelmyvar
(inconfig.cfg
) in op-e
en je ziet een lege regel, omdatecho
zal denken dat het “een vlag is. Maarprintf
heeft dat probleem niet. Deprintf --
zegt “print this, en” interpreteer niets als vlaggen “, en de"%s\n"
zegt” formatteer de uitvoer als een string met een nieuwe regel aan het einde, en tot slot is de laatste parameter de waarde die printf moet opmaken. - Als je geen waarden op het scherm wilt weergeven, zou je ze gewoon normaal toewijzen, zoals
myvar="$(config_get myvar)";
. Als je ze naar het scherm gaat afdrukken, raad ik aan om printf te gebruiken om volledig veilig te zijn tegen echo-incompatibele strings die in de gebruikersconfiguratie kunnen staan. Maar echo is prima als de door de gebruiker verstrekte variabele niet is het eerste teken van de tekenreeks die u gebruikt, aangezien dat “de enige situatie is waarin” vlaggen “kunnen worden geïnterpreteerd, dus zoiets alsecho "foo: $(config_get myvar)";
is veilig, aangezien de” foo “begint” niet met een streepje en vertelt daarom echo dat de rest van de string er ook geen vlag voor is. 🙂
Opmerkingen
- @ user2993656 Bedankt dat je hebt opgemerkt dat mijn originele code nog steeds mijn persoonlijke configuratiebestandsnaam (environment.cfg) bevatte in plaats van de juiste. Wat betreft de ” echo -n ” bewerking die je hebt gedaan, dat hangt af van de gebruikte shell. Op Mac / Linux Bash, ” echo -n ” betekent ” echo zonder nieuwe regel “, wat ik deed om te voorkomen dat er nieuwe regels achter zouden lopen. Maar het lijkt zonder dit precies hetzelfde te werken, dus bedankt voor de bewerkingen!
- Eigenlijk heb ik het gewoon doorgenomen en herschreven om printf te gebruiken in plaats van echo, wat ervoor zorgt dat we ‘ Ik zal het risico wegnemen dat echo ” vlaggen ” in de configuratiewaarden verkeerd interpreteert.
- Ik hou echt van deze versie. Ik heb de
config.cfg.defaults
laten vallen in plaats van ze te definiëren op het moment dat ik$(config_get var_name "default_value")
aanriep. tritarget.org/static/… - Evenzo – dit is geweldig.
Antwoord
De meest gebruikelijke, efficiënte en correcte manier is om source
, of .
als een verkorte vorm. Bijvoorbeeld:
source /home/myuser/test/config
of
. /home/myuser/test/config
Iets om te overwegen is echter de beveiligingsproblemen die het gebruik van een extra extern afkomstig configuratiebestand kan veroorzaken, aangezien er aanvullende code kan worden ingevoegd. Voor meer informatie, waaronder hoe u dit probleem kunt detecteren en oplossen, raad ik u aan een kijkje te nemen in het gedeelte “Beveiligen” van http://wiki.bash-hackers.org/howto/conffile#secure_it
Reacties
- Ik had hoge verwachtingen van dat artikel (kwam ook voor in mijn zoekresultaten), maar de auteur ‘ s suggestie om te proberen regex te gebruiken om kwaadaardige code eruit te filteren is een oefening in zinloosheid.
- De procedure met punt, vereist een absoluut pad?Met de relatieve werkt het niet ‘ niet
Antwoord
Ik gebruik dit in mijn 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 }
Moet elke tekencombinatie ondersteunen, behalve dat sleutels “t =
in hen, want dat is de scheidingsteken. Al het andere werkt.
% 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
Dit is ook volkomen veilig omdat het geen source
of eval
.
Reacties
- Is niet ‘ dit veronderstelde om eerst het configuratiebestand te maken als het niet ‘ niet bestaat? Het niet ‘ t, maar
touch -a "${path}"
zal er natuurlijk voor zorgen dat het bestaat, ook zonder de mtime frivool bij te werken.
Answer
Dit is beknopt en veilig:
# 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`)
De -i
zorgt ervoor dat u alleen de variabelen krijgt van common.vars
Update: een illustratie van de beveiliging is dat
env -i "touch evil1 foo=omg boo=$(touch evil2)"
Geen aangeraakte bestanden zal produceren. Getest op mac met bash, dwz met behulp van bsd env.
Reacties
- Kijk maar eens hoe evil1 en evil2 bestanden worden gemaakt als je dit in comm on.vars “ touch evil1 foo = omg boo = $ (touch evil2)
- @pihentagy Voor mij produceert het volgende geen aangeraakte bestanden
env -i 'touch evil1 foo=omg boo=$(touch evil2)'
. Draait op mac. - inderdaad, maar heeft geen toegang tot foo. Ik ‘ heb
env -i ... myscript.sh
geprobeerd en binnen dat script is foo niet gedefinieerd. Als u echter ” garbage ” verwijdert, zal het werken. Dus bedankt voor het uitleggen. : +1:
Answer
De meeste gebruikers, hoewel niet veel containers, hebben al de git
binair. Gebruik daarom git config
voor applicatieconfiguratiebeheer met behulp van een speciaal niet-conflicterend configuratiebestand zoals in de onderstaande voorbeelden?
# 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
Voor aanvullende commandos, zie de man bladzijde. Het is verstandig om er eerst voor te zorgen dat het configuratiebestand bestaat:
touch -a ~/.myapp
Antwoord
Deze lijkt veilig en kort. Voel je vrij om dit meedogenloos te kraken. Ik “zou graag een betere manier willen weten.
TL; DR;
while read LINE; do declare "$LINE"; done < evil.conf
Ik gebruik bash 4.3.48.
Het is ook compatibel met bash --posix
. Zie onderaan voor test.
Maar sh
ondersteunt het niet vanwege declare
.
Basistest voor degenen die bewijs willen
Maak een bestand 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"
Laad de configuratie met het fragment
while read LINE; do declare "$LINE"; done < evil.conf
Uitvoer (zie sanitizer in actie)
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
Laten we nu de waarden controleren
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"
Controleer bijwerkingen (geen bijwerkingen):
ls evil evil2
ls: cannot access "evil": No such file or directory ls: cannot access "evil2": No such file or directory
Bijlage. 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
Antwoord
Voor mijn scenario source
of .
w prima, maar ik wilde lokale omgevingsvariabelen ondersteunen (dwz FOO=bar myscript.sh
) die voorrang hebben op geconfigureerde variabelen. Ik wilde ook dat het configuratiebestand door de gebruiker bewerkbaar en comfortabel was voor iemand die gewend was configuratiebestanden te vinden, en om het zo klein / eenvoudig mogelijk te houden, om niet af te leiden van de hoofdlijnen van mijn zeer kleine script.
Dit is wat ik bedacht:
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)
In wezen – het controleert op variabele definities (zonder erg flexibel te zijn over witruimte) en herschrijft die regels zodat de waarde wordt geconverteerd naar een standaardwaarde voor die variabele, en de variabele is ongewijzigd als deze wordt gevonden, zoals de XDG_CONFIG_HOME
variabele hierboven. Het haalt deze gewijzigde versie van het configuratiebestand op en gaat verder.
Toekomstig werk zou het sed
script robuuster kunnen maken, regels eruit filteren die er vreemd uitzien of niet ” t definities, enz., breken niet bij opmerkingen aan het einde van de regel – maar dit is voorlopig goed genoeg voor mij.
Antwoord
U kunt het doen:
#!/bin/bash name="mohsen" age=35 cat > /home/myuser/test/config << EOF Name=$name Age=$age EOF
*
in invoer niet correct kan verwerken, maar dan wat in Bash verwerkt dat karakter goed?my\\password