Potřebuji vytvořit konfigurační soubor pro svůj vlastní skript:
Zde je příklad:
script:
#!/bin/bash source /home/myuser/test/config echo "Name=$nam" >&2 echo "Surname=$sur" >&2
Obsah /home/myuser/test/config
:
nam="Mark" sur="Brown"
to funguje!
Moje otázka: je to správný způsob, jak udělejte to nebo tam „jinými způsoby?
Komentáře
- Proměnné by měly být nahoře. I ‚ m překvapeno, že to funguje. Mimochodem, proč potřebujete konfigurační soubor? Plánujete použít tyto proměnné někde jinde?
- Faheem, potřebuji proměnné, protože můj skript má mnoho možností: použití a konfigurační soubor rozdělí skript na skript. Díky
- IMHO je v pořádku. Udělal bych to takhle.
-
abcde
to také dělá takto a to je docela velký program (pro shell skript). Můžete se na něj podívat zde .
Odpověď
source
není zabezpečená, protože provede libovolný kód. To vás nemusí znepokojovat, ale pokud jsou oprávnění k souborům nesprávná, může útočník s přístupem k souborovému systému spustit kód jako privilegovaný uživatel vložením kódu do konfiguračního souboru načteného jinak zabezpečeným skriptem, například Init script.
Doposud nejlepším řešením, které jsem dokázal identifikovat, je neohrabané řešení reinventing-the-wheel:
myscript.conf
password=bar echo rm -rf / PROMPT_COMMAND="echo "Sending your last command $(history 1) to my email"" hostname=localhost; echo rm -rf /
Pomocí source
, spustilo by se to echo rm -rf /
dvakrát a zároveň by se změnil běžící uživatel $PROMPT_COMMAND
. Místo toho proveďte toto:
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 (kompatibilní s Mac / Bash 3)
#!/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
Odpovězte, pokud v mém kódu najdete bezpečnostní zneužití.
Komentáře
Odpovědět
Analyzovat konfigurační soubor, neprovádět jej.
V současné době píšu v práci aplikaci, která používá extrémně jednoduchou konfiguraci XML:
<config> <username>username-or-email</username> <password>the-password</password> </config>
Ve skriptu shellu (dále jen „aplikace“) se k uživatelskému jménu dostanu takto (více nebo méně, vložil jsem to do funkce shellu):
username="$( xml sel -t -v "/config/username" "$config_file" )"
Příkaz xml
je XMLStarlet , který je k dispozici pro většinu Unices.
Používám XML, protože ostatní části aplikace také pracují s daty zakódovanými v souborech XML, takže bylo to nejjednodušší.
Pokud dáváte přednost formátu JSON, existuje jq
což je snadno použitelný syntaktický analyzátor JSON.
Můj konfigurační soubor by v JS vypadal nějak takto ZAPNUTO:
{ "username": "username-or-email", "password": "the-password" }
A pak ve skriptu získám uživatelské jméno:
username="$( jq -r ".username" "$config_file" )"
Komentáře
- Spuštění skriptu má řadu výhod a nevýhod. Hlavní nevýhody jsou zabezpečení, pokud někdo může změnit konfigurační soubor, může spustit kód a je těžší udělat z něj idiotský důkaz. Výhodou je rychlost, při jednoduchém testu je více než 10 000krát rychlejší získání konfiguračního souboru než spuštění pq a flexibilita, ocení to každý, kdo má rád opičí opravu pythonu.
- @icarus Jak velký konfigurační soubory, se kterými se obvykle setkáváte, a jak často je musíte analyzovat v jedné relaci? Všimněte si také, že z XML nebo JSON může být najednou několik hodnot.
- Obvykle jen několik (1 až 3) hodnot. Pokud používáte
eval
k nastavení více hodnot, spouštíte vybrané části konfiguračního souboru :-). - @icarus Přemýšlel jsem o polích … Není třeba nic
eval
. Výkonnostní hit použití standardního formátu s existujícím analyzátorem (i když je ‚ s externím nástrojem) je zanedbatelný ve srovnání s robustností, množstvím kódu a snadným použitím a udržovatelnost. - +1 pro “ analyzovat konfigurační soubor, ‚ jej nespustit “
Odpověď
Zde je čistá a přenosná verze kompatibilní s Bash 3 a nahoru, na počítačích Mac i Linux.
Specifikuje všechny výchozí hodnoty v samostatném souboru, aby se ve všech vašich skriptech prostředí nepotřebovala obrovská přeplněná duplicitní konfigurační funkce „výchozí“. Umožňuje vám vybrat si mezi čtením s výchozími záložními zdroji nebo bez nich:
config.cfg :
myvar=Hello World
config.cfg.defaults :
myvar=Default Value othervar=Another Variable
config.shlib (toto je knihovna, takže neexistuje žádný řádek shebang):
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 (nebo jakékoli skripty, kam chcete číst konfigurační hodnoty) :
#!/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
Vysvětlení testovacího skriptu:
- Všimněte si, že všechna použití config_get v test.sh jsou uvedena v uvozovkách. Zabalením každého config_get do uvozovek zajistíme, že text v hodnotě proměnné nebude nikdy chybně interpretován jako příznaky. A zajišťuje, že mezery v hodnotě konfigurace řádně zachováme, například více mezer za sebou.
- A co je to za
printf
řádek? „něco, čeho byste si měli být vědomi:echo
je špatný příkaz pro tisk textu, nad kterým nemáte žádnou kontrolu. I když použijete uvozovky, bude to interpretovat příznaky. Zkuste nastavitmyvar
(vconfig.cfg
) na-e
a uvidíte prázdný řádek, protožeecho
si bude myslet, že je to příznak. Aleprintf
tento problém nemá.printf --
říká „vytiskněte to a nic nevykládejte jako příznaky“ a"%s\n"
říká „formátujte výstup jako řetězec s koncovým novým řádkem a konečným parametrem je hodnota, kterou má formát printf formátovat. - Pokud na obrazovce nebudete odrážet hodnoty, jednoduše je přiřadíte normálně, například
myvar="$(config_get myvar)";
. Pokud je chcete tisknout na obrazovku, doporučuji použít printf, abyste byli zcela v bezpečí proti jakýmkoli řetězcům nekompatibilním s ozvěnou, které mohou být v uživatelské konfiguraci. Ale ozvěna je v pořádku, pokud uživatelská proměnná není první znak řetězce, který ozýváte, protože to je jediná situace, kdy lze „příznaky“ interpretovat, takže něco jakoecho "foo: $(config_get myvar)";
je bezpečné, protože “ foo „nezačíná pomlčkou, a proto říká echo, že ani zbytek řetězce jej nenaznačuje. 🙂
Komentáře
- @ user2993656 Děkujeme, že jste si všimli, že v mém původním kódu byl stále můj soukromý konfigurační název souboru (environment.cfg) namísto správného. Pokud jde o “ echo -n “ úpravy, které jste provedli, záleží na použitém prostředí. V systému Mac / Linux Bash “ echo -n “ znamená “ ozvěnu bez koncové řádky „, což jsem udělal, abych se vyhnul koncům nových řádků. Ale zdá se, že to funguje úplně stejně, takže díky za úpravy!
- Vlastně jsem to prošel a přepsal, abych místo echa použil printf, což zajišťuje, že jsme ‚ Zbavím se rizika nesprávné interpretace ozvěny “ příznaků “ v hodnotách konfigurace.
- Opravdu se mi tato verze líbí. Namísto jejich definování v době volání
$(config_get var_name "default_value")
jsemconfig.cfg.defaults
upustil. tritarget.org/static/… - Podobně – to je skvělé.
Odpověď
Nejběžnějším, nejúčinnějším a správným způsobem je použití source
, nebo .
jako zkratkový formulář. Například:
source /home/myuser/test/config
nebo
. /home/myuser/test/config
Je však třeba vzít v úvahu problémy se zabezpečením, které může způsobit použití dalšího externího konfiguračního souboru, vzhledem k tomu, že lze vložit další kód. Další informace, včetně informací o tom, jak tento problém zjistit a vyřešit, doporučuji nahlédnout do sekce „Zabezpečit“ v http://wiki.bash-hackers.org/howto/conffile#secure_it
Komentáře
- Do tohoto článku jsem vkládal velké naděje (objevil se také ve výsledcích vyhledávání), ale autor ‚ pokusu o odfiltrování škodlivého kódu pomocí regexu je marné cvičení.
- Postup s tečkou vyžaduje absolutní cestu?S relativním nefunguje ‚ nefunguje
Odpovědět
Používám to ve svých skriptech:
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 }
Mělo by podporovat každou kombinaci znaků, kromě toho, že klíče nemohou mít =
v nich, protože to je oddělovač. Funguje cokoli jiného.
% 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
Je to také zcela bezpečné, protože nepoužívá source
nebo eval
.
Komentáře
- Není to ‚ nejprve vytvořit konfigurační soubor, pokud ‚ neexistuje? ‚ t, ale
touch -a "${path}"
samozřejmě zajistí, že existuje, a to i bez frivolní aktualizace jeho času.
Odpověď
Toto je stručně a bezpečně:
# 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
zajišťuje, že dostanete pouze proměnné z common.vars
Aktualizace: Ilustrace zabezpečení je ta, že
env -i "touch evil1 foo=omg boo=$(touch evil2)"
Nevytvoří žádné dotčené soubory. Testováno na Macu s bash, tj. pomocí bsd env.
Komentáře
- Podívejte se, jak se vytvářejí soubory evil1 a evil2, pokud to zadáte do komunikace on.vars „ touch evil1 foo = omg boo = $ (touch evil2)
- @pihentagy Pro mě následující nevytvoří žádné soubory, kterých se dotknete
env -i 'touch evil1 foo=omg boo=$(touch evil2)'
. Běží na macu. - skutečně, ale nemá přístup k foo. ‚ Zkoušel jsem
env -i ... myscript.sh
a uvnitř tohoto skriptu foo není definován. Pokud však odstraníte “ odpadky „, bude to fungovat. Takže díky za vysvětlení. : +1:
Odpověď
Většina uživatelů, i když jich není mnoho, již git
binární. Proč tedy nepoužívat git config
pro správu konfigurace aplikace pomocí vyhrazeného nekonfliktního konfiguračního souboru jako v příkladech níže?
# 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
Další příkazy najdete v jeho manuálu strana. Je rozumné nejprve se ujistit, že konfigurační soubor existuje:
touch -a ~/.myapp
Odpovědět
Tenhle se zdá bezpečný a krátký. Neváhejte to bezohledně prolomit. Chtěl bych vědět o lepším způsobu.
TL; DR;
while read LINE; do declare "$LINE"; done < evil.conf
Používám bash 4.3.48.
Je také kompatibilní s bash --posix
. Test naleznete níže.
Ale sh
nepodporuje to kvůli declare
.
Základní test pro ty, kteří chtějí důkaz
Vytvořit soubor 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"
Načtěte konfiguraci pomocí úryvku
while read LINE; do declare "$LINE"; done < evil.conf
Výstup (viz sanitizer v akci)
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
Nyní můžeme zkontrolovat hodnoty
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"
Zkontrolovat vedlejší účinky (žádné vedlejší účinky):
ls evil evil2
ls: cannot access "evil": No such file or directory ls: cannot access "evil2": No such file or directory
Příloha. Vyzkoušejte 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
Odpověď
Podle mého scénáře source
nebo .
w jako v pořádku, ale chtěl jsem podporovat místní proměnné prostředí (tj. FOO=bar myscript.sh
), které mají přednost před konfigurovanými proměnnými. Také jsem chtěl, aby byl konfigurační soubor uživatelsky upravitelný a pohodlný pro někoho, kdo byl zvyklý získávat konfigurační soubory, a aby byl co nejmenší / nejjednodušší, aby to neodvádělo pozornost od hlavního tahu mého velmi malého skriptu.
To je to, s čím jsem přišel:
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)
V podstatě – kontroluje definice proměnných (aniž by byl velmi flexibilní ohledně mezer) a přepíše tyto řádky tak, aby hodnota se pro tuto proměnnou převede na výchozí hodnotu a proměnná se při úpravě nemodifikuje, podobně jako výše uvedená proměnná XDG_CONFIG_HOME
. Poskytuje zdroj této změněné verze konfiguračního souboru a pokračuje dál.
Díky budoucí práci by mohl být skript sed
robustnější, odfiltrovat řádky, které vypadají divně nebo jinak. “ Definice atd. se nepřerušují na konci řádku – ale prozatím je to pro mě dost dobré.
Odpovědět
Můžete to udělat:
#!/bin/bash name="mohsen" age=35 cat > /home/myuser/test/config << EOF Name=$name Age=$age EOF
*
správně zpracovávat vstupy, ale pak co v Bash zvládá tento znak dobře?my\\password