Ho bisogno di creare un file di configurazione per il mio script:
Ecco un esempio:
script:
#!/bin/bash source /home/myuser/test/config echo "Name=$nam" >&2 echo "Surname=$sur" >&2
Contenuto di /home/myuser/test/config
:
nam="Mark" sur="Brown"
che funziona!
La mia domanda: è questo il modo corretto di fare questo o ci sono altri modi?
Commenti
Answer
source
non è sicuro in quanto eseguirà codice arbitrario. Questo potrebbe non essere un problema per te, ma se i permessi dei file non sono corretti, potrebbe essere possibile per un utente malintenzionato con accesso al file system eseguire codice come utente privilegiato inserendo codice in un file di configurazione caricato da uno script altrimenti protetto come un init script.
Finora, la migliore soluzione che sono stato in grado di identificare è la goffa soluzione reinventare la ruota:
myscript.conf
password=bar echo rm -rf / PROMPT_COMMAND="echo "Sending your last command $(history 1) to my email"" hostname=localhost; echo rm -rf /
Utilizzando source
, questo verrebbe eseguito echo rm -rf /
due volte, oltre a modificare lutente in esecuzione “s $PROMPT_COMMAND
. Invece, fai questo:
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 (compatibile con 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
Per favore rispondi se trovi un exploit di sicurezza nel mio codice.
Commenti
- Cordiali saluti, questa è una soluzione Bash versione 4.0 che purtroppo è soggetta a problemi di licenza insani imposti da Apple e non è disponibile per impostazione predefinita su Mac
- @Sukima Buon punto. Ho ‘ ho aggiunto una versione compatibile con Bash 3. Il suo punto debole è che non gestisce correttamente
*
negli input, ma poi cosa in Bash gestisce bene quel carattere? - Il primo script fallisce se la password contiene una barra rovesciata.
- @Kusalananda E se la barra rovesciata è sfuggita?
my\\password
- Questa versione impiega diversi secondi per elaborare il mio file di configurazione, ho scoperto che la soluzione di gw0 è molto più veloce.
Risposta
Analizza il file di configurazione, non eseguirlo.
Attualmente sto scrivendo unapplicazione al lavoro che utilizza una configurazione XML estremamente semplice:
<config> <username>username-or-email</username> <password>the-password</password> </config>
Nello script della shell (l “applicazione”), questo è ciò che faccio per ottenere il nome utente (più o less, lho “inserito in una funzione shell):
username="$( xml sel -t -v "/config/username" "$config_file" )"
Il comando xml
è XMLStarlet , disponibile per la maggior parte degli Unix.
Utilizzo XML poiché anche altre parti dellapplicazione si occupano di dati codificati in file XML, quindi è stato più semplice.
Se preferisci JSON, cè “s jq
che è un parser JSON della shell facile da usare.
Il mio file di configurazione sarebbe simile a questo in JS ON:
{ "username": "username-or-email", "password": "the-password" }
E poi “vorrei ottenere il nome utente nello script:
username="$( jq -r ".username" "$config_file" )"
Commenti
- Lesecuzione dello script presenta una serie di vantaggi e svantaggi. I principali svantaggi sono la sicurezza, se qualcuno può alterare il file di configurazione, può eseguire il codice ed è più difficile renderlo a prova di idiota. I vantaggi sono la velocità, in un semplice test è più di 10.000 volte più veloce generare un file di configurazione che eseguire pq, e la flessibilità, chiunque ami le patch di scimmie python lo apprezzerà.
- @icarus Quanto è grande file di configurazione che incontri di solito e quanto spesso hai bisogno di analizzarli in una sessione? Si noti inoltre che diversi valori possono essere ricavati da XML o JSON in una volta sola.
- Di solito solo pochi (da 1 a 3) valori. Se stai usando
eval
per impostare più valori, stai eseguendo parti selezionate del file di configurazione :-). - @icarus stavo pensando agli array … Non è necessario
eval
nulla. Limpatto sulle prestazioni dellutilizzo di un formato standard con un parser esistente (anche se ‘ è unutilità esterna) è trascurabile rispetto alla robustezza, alla quantità di codice, alla facilità duso e manutenibilità. - +1 per ” analizzare il file di configurazione, don ‘ t eseguirlo ”
Risposta
Ecco una versione pulita e portatile compatibile con Bash 3 e up, sia su Mac che su Linux.
Specifica tutti i valori di default in un file separato, per evitare la necessità di una funzione di configurazione “default” enorme, disordinata e duplicata in tutti gli script della shell. Inoltre, ti consente di scegliere tra la lettura con o senza i fallback predefiniti:
config.cfg :
myvar=Hello World
config.cfg.defaults :
myvar=Default Value othervar=Another Variable
config.shlib (questa è una libreria, quindi non cè nessuna riga):
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 (o qualsiasi script in cui desideri leggi i valori di configurazione) :
#!/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
Spiegazione dello script di test:
- Nota che tutti gli usi di config_get in test.sh sono racchiusi tra virgolette doppie. Racchiudendo ogni config_get tra virgolette doppie, ci assicuriamo che il testo nel valore della variabile mai venga interpretato erroneamente come flag. E assicura che conserviamo correttamente gli spazi bianchi, ad esempio più spazi di fila nel valore di configurazione.
- E cosè quella riga
printf
? “è qualcosa di cui dovresti essere a conoscenza:echo
è un cattivo comando per stampare testo su cui non hai alcun controllo. Anche se usi le virgolette doppie, interpreterà i flag. Prova a impostaremyvar
(inconfig.cfg
) su-e
e vedrai una riga vuota, perchéecho
penserà che sia “un flag. Maprintf
non ha questo problema.printf --
dice “stampa questo e non” interpretare nulla come flag “e"%s\n"
dice” formatta loutput come una stringa con una nuova riga finale, e infine il parametro finale è il valore che printf deve formattare. - Se non stai facendo eco a valori sullo schermo, allora devi semplicemente assegnarli normalmente, come
myvar="$(config_get myvar)";
. Se vuoi stamparli sullo schermo, ti suggerisco di usare printf per essere totalmente sicuro contro qualsiasi stringa incompatibile con leco che potrebbe essere nella configurazione dellutente. Ma echo va bene se la variabile fornita dallutente non è “t il primo carattere della stringa che stai ripetendo, poiché “è lunica situazione in cui” flags “potrebbe essere interpretato, quindi qualcosa comeecho "foo: $(config_get myvar)";
è sicuro, poiché” foo “non” inizia con un trattino e quindi dice a echo che anche il resto della stringa non è “t flag”. 🙂
Commenti
- @ user2993656 Grazie per aver notato che il mio codice originale conteneva ancora il mio nome file di configurazione privato (environment.cfg) invece di quello corretto. Come per il ” echo -n ” modifica eseguita, dipende dalla shell utilizzata. Su Mac / Linux Bash, ” echo -n ” significa ” echo senza fine riga “, cosa che ho fatto per evitare di finire le nuove righe. Ma sembra funzionare esattamente allo stesso modo senza di essa, quindi grazie per le modifiche!
- In realtà, lho appena riscritto per utilizzare printf anziché echo, il che garantisce che ‘ elimina il rischio di uninterpretazione errata delleco di ” flag ” nei valori di configurazione.
- Mi piace molto questa versione. Ho eliminato
config.cfg.defaults
invece di definirli al momento della chiamata a$(config_get var_name "default_value")
. tritarget.org/static/… - Allo stesso modo, è fantastico.
Risposta
Il modo più comune, efficiente e corretto è utilizzare source
o .
come forma abbreviata. Ad esempio:
source /home/myuser/test/config
o
. /home/myuser/test/config
Qualcosa da considerare, tuttavia, è il problemi di sicurezza che possono sollevare utilizzando un file di configurazione esterno aggiuntivo, dato che è possibile inserire codice aggiuntivo. Per ulteriori informazioni, incluso su come rilevare e risolvere questo problema, ti consiglio di dare unocchiata alla sezione “Proteggilo” di http://wiki.bash-hackers.org/howto/conffile#secure_it
Commenti
- Avevo grandi speranze per quellarticolo (è apparso anche nei miei risultati di ricerca), ma lautore ‘ di tentare di utilizzare regex per filtrare il codice dannoso è un esercizio inutile.
- La procedura con punto, richiede un percorso assoluto?Con quello relativo non ‘ non funziona
Risposta
Lo uso nei miei script:
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 }
Dovrebbe supportare ogni combinazione di caratteri, eccetto le chiavi che non possono =
in loro, poiché quello è il separatore. Qualsiasi altra cosa funziona.
% 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
Inoltre, questo è completamente sicuro poiché “t non utilizza source
o eval
.
Commenti
- Non è ‘ questo supposto per creare prima il file di configurazione se ‘ non esiste? Non ‘ t, ma
touch -a "${path}"
ovviamente garantirà che esista, anche senza aggiornare in modo frivolo il suo mtime.
Answer
Questo è succinto e sicuro:
# 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
ti assicura di ottenere solo le variabili da common.vars
Aggiornamento: un esempio della sicurezza è che
env -i "touch evil1 foo=omg boo=$(touch evil2)"
non produrrà alcun file modificato. Testato su mac con bash, cioè usando bsd env.
Commenti
- Guarda come vengono creati i file evil1 e evil2 se lo metti in comm on.vars “ touch evil1 foo = omg boo = $ (touch evil2) “
- @pihentagy Per me quanto segue non produce file modificati
env -i 'touch evil1 foo=omg boo=$(touch evil2)'
. Funziona su mac. - in effetti, ma non è possibile accedere a foo. Ho ‘ ho provato
env -i ... myscript.sh
e allinterno di quello script foo non è definito. Tuttavia, se rimuovi ” garbage “, funzionerà. Quindi grazie per aver spiegato. : +1:
Risposta
La maggior parte degli utenti, sebbene non molti contenitori, hanno già git
binario. Perché non utilizzare quindi git config
per la gestione della configurazione dellapplicazione utilizzando un file di configurazione dedicato non in conflitto come negli esempi seguenti?
# 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
Per ulteriori comandi, vedere il suo man pagina. È consigliabile prima assicurarsi che il file di configurazione esista:
touch -a ~/.myapp
Risposta
Questo sembra corto e sicuro. Sentiti libero di risolvere questo problema senza pietà. “Vorrei conoscere un modo migliore.
TL; DR;
while read LINE; do declare "$LINE"; done < evil.conf
Sto usando bash 4.3.48.
È anche conforme a bash --posix
. Vedi in basso per il test.
Ma sh
non lo supporta a causa di declare
.
Test di base per chi vuole una prova
Crea file 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"
Carica la configurazione con lo snippet
while read LINE; do declare "$LINE"; done < evil.conf
Risultato (vedi disinfettante in azione)
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
Controlliamo ora i valori
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"
Controlla gli effetti collaterali (senza effetti collaterali):
ls evil evil2
ls: cannot access "evil": No such file or directory ls: cannot access "evil2": No such file or directory
Appendice. 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
Risposta
Per il mio scenario, source
o .
w altrettanto bene, ma volevo supportare le variabili di ambiente locali (ad esempio, FOO=bar myscript.sh
) avendo la precedenza sulle variabili configurate. Volevo anche che il file di configurazione fosse modificabile dallutente e comodo per qualcuno abituato ai file di configurazione di origine, e che lo mantenesse il più piccolo / semplice possibile, per non distrarre dalla spinta principale del mio piccolissimo script.
Questo è ciò che mi è venuto in mente:
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)
Essenzialmente – controlla le definizioni delle variabili (senza essere molto flessibile sugli spazi bianchi) e riscrive quelle righe in modo che il il valore viene convertito in un valore predefinito per quella variabile e la variabile non viene modificata se trovata, come la variabile XDG_CONFIG_HOME
sopra. Fornisce questa versione modificata del file di configurazione e continua.
Il lavoro futuro potrebbe rendere lo script sed
più robusto, filtrare le righe che sembrano strane o non sono ” Le definizioni, ecc., non interrompono i commenti di fine riga, ma per ora è abbastanza buono per me.
Risposta
Puoi farlo:
#!/bin/bash name="mohsen" age=35 cat > /home/myuser/test/config << EOF Name=$name Age=$age EOF
abcde
lo fa anche in questo modo e è un programma abbastanza grande (per uno script di shell). Puoi dare unocchiata qui .