Konfigurationsdatei für mein Shell-Skript verwenden

Ich muss eine Konfigurationsdatei für mein eigenes Skript erstellen:
Hier ein Beispiel:

script:

#!/bin/bash source /home/myuser/test/config echo "Name=$nam" >&2 echo "Surname=$sur" >&2 

Inhalt von /home/myuser/test/config:

nam="Mark" sur="Brown" 

das funktioniert!

Meine Frage: ist dies der richtige Weg Tun Sie dies oder gibt es andere Möglichkeiten?

Kommentare

  • Die Variablen sollten oben stehen. I ‚ Ich bin überrascht, dass es funktioniert. Wie auch immer, warum benötigen Sie eine Konfigurationsdatei? Planen Sie, diese Variablen woanders zu verwenden?
  • Faheem, ich brauche die Variablen, weil mein Skript viele Optionen hat: Verwenden von a Die Konfigurationsdatei vereinfacht das Skript. Danke
  • IMHO, es ist in Ordnung. Ich würde dies tun.
  • abcde macht es auch so und Das ist ein ziemlich großes Programm (für ein Shell-Skript). Sie können es hier ansehen.

Antwort

source ist nicht sicher, da beliebiger Code ausgeführt wird. Dies ist möglicherweise kein Problem für Sie. Wenn jedoch die Dateiberechtigungen nicht korrekt sind, kann ein Angreifer mit Dateisystemzugriff möglicherweise Code als privilegierter Benutzer ausführen, indem er Code in eine Konfigurationsdatei einfügt, die von einem ansonsten gesicherten Skript wie z Init-Skript.

Die beste Lösung, die ich bisher identifizieren konnte, ist die ungeschickte Neuerfindung des Rads:

myscript.conf

password=bar echo rm -rf / PROMPT_COMMAND="echo "Sending your last command $(history 1) to my email"" hostname=localhost; echo rm -rf / 

Verwenden von source, Dies würde echo rm -rf / zweimal ausführen und die $PROMPT_COMMAND des laufenden Benutzers ändern. Gehen Sie stattdessen folgendermaßen vor:

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 

Bitte antworten Sie, wenn Sie in meinem Code einen Sicherheits-Exploit finden.

Kommentare

  • Zu Ihrer Information, dies ist eine Bash-Version 4.0-Lösung, die leider wahnsinnigen Lizenzproblemen unterliegt, die von Apple auferlegt wurden und auf Macs
  • @Sukima Guter Punkt. Ich ‚ habe eine Version hinzugefügt, die mit Bash 3 kompatibel ist. Seine Schwäche ist, dass * in Eingaben nicht richtig verarbeitet wird, aber dann Was in Bash geht gut mit diesem Zeichen um?
  • Das erste Skript schlägt fehl, wenn das Kennwort einen Backslash enthält.
  • @Kusalananda Was ist, wenn der Backslash maskiert wird? my\\password
  • Diese Version benötigt einige Sekunden, um meine Konfigurationsdatei zu verarbeiten. Ich fand, dass die Lösung von gw0 viel schneller ist.

Antwort

Analysieren Sie die Konfigurationsdatei, führen Sie sie nicht aus.

Ich schreibe gerade eine Anwendung bei der Arbeit, die verwendet eine extrem einfache XML-Konfiguration:

<config> <username>username-or-email</username> <password>the-password</password> </config> 

Im Shell-Skript (der „Anwendung“) gehe ich wie folgt vor, um den Benutzernamen zu erhalten (mehr oder weniger, ich habe es in eine Shell-Funktion eingefügt):

username="$( xml sel -t -v "/config/username" "$config_file" )" 

Der Befehl xml lautet XMLStarlet , das für die meisten Unices verfügbar ist.

Ich verwende XML, da andere Teile der Anwendung auch Daten verarbeiten, die in XML-Dateien codiert sind Es war am einfachsten.

Wenn Sie JSON bevorzugen, gibt es „s jq Einfach zu verwendender Shell-JSON-Parser.

Meine Konfigurationsdatei würde in JS ungefähr so aussehen ON:

{ "username": "username-or-email", "password": "the-password" } 

Und dann würde ich den Benutzernamen im Skript erhalten:

username="$( jq -r ".username" "$config_file" )" 

Kommentare

  • Die Ausführung des Skripts hat eine Reihe von Vor- und Nachteilen. Die Hauptnachteile sind die Sicherheit. Wenn jemand die Konfigurationsdatei ändern kann, kann er Code ausführen, und es ist schwieriger, ihn idiotensicher zu machen. Die Vorteile sind Geschwindigkeit, bei einem einfachen Test ist es mehr als 10.000 Mal schneller, eine Konfigurationsdatei zu erstellen als pq auszuführen, und Flexibilität. Jeder, der Affen-Patching-Python mag, wird dies zu schätzen wissen.
  • @icarus Wie groß Konfigurationsdateien, auf die Sie normalerweise stoßen, und wie oft müssen Sie sie in einer Sitzung analysieren? Beachten Sie auch, dass mehrere Werte auf einmal aus XML oder JSON stammen können.
  • Normalerweise nur wenige (1 bis 3) Werte. Wenn Sie eval verwenden, um mehrere Werte festzulegen, führen Sie ausgewählte Teile der Konfigurationsdatei aus :-).
  • @icarus Ich dachte, Arrays … Keine Notwendigkeit, eval irgendetwas. Der Leistungseinbruch bei der Verwendung eines Standardformats mit einem vorhandenen Parser (obwohl ‚ ein externes Dienstprogramm ist) ist im Vergleich zu der Robustheit, der Codemenge und der Benutzerfreundlichkeit vernachlässigbar und Wartbarkeit.
  • +1 für “ Analysieren Sie die Konfigurationsdatei, ‚ führen Sie sie nicht aus “

Antwort

Hier ist eine saubere und tragbare Version, die mit Bash 3 und kompatibel ist sowohl unter Mac als auch unter Linux.

Es gibt alle Standardeinstellungen in einer separaten Datei an, um zu vermeiden, dass in all Ihren Shell-Skripten eine große, überfüllte, duplizierte Konfigurationsfunktion für „Standardeinstellungen“ erforderlich ist. Außerdem können Sie zwischen dem Lesen mit oder ohne Standard-Fallbacks wählen:

config.cfg :

myvar=Hello World 

config.cfg.defaults :

myvar=Default Value othervar=Another Variable 

config.shlib (dies ist also eine Bibliothek Es gibt keine Shebang-Zeile):

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 (oder Skripte, bei denen Sie möchten Konfigurationswerte lesen) :

#!/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 

Erläuterung des Testskripts:

    eachten Sie, dass alle Verwendungen von config_get in test.sh in doppelte Anführungszeichen gesetzt werden. Indem wir jedes config_get in doppelte Anführungszeichen setzen, stellen wir sicher, dass Text im Variablenwert nie als Flags falsch interpretiert wird. Und es stellt sicher, dass Leerzeichen ordnungsgemäß beibehalten werden, z. B. mehrere Leerzeichen in einer Reihe im Konfigurationswert.

  • Und was ist diese printf -Zeile? „s etwas, das Sie beachten sollten: echo ist ein schlechter Befehl zum Drucken von Text, über den Sie keine Kontrolle haben. Selbst wenn Sie doppelte Anführungszeichen verwenden, werden Flags interpretiert. Versuchen Sie, myvar (in config.cfg) auf -e zu setzen, und Sie sehen eine leere Zeile. weil echo denken wird, dass es „ein Flag ist. Aber printf hat dieses Problem nicht. Das printf -- sagt „print this und interpretiere nichts als Flags“, und das "%s\n" sagt „formatiere die Ausgabe als String mit einem nachgestellten Zeilenumbruch, und schließlich ist der letzte Parameter der Wert, den printf formatieren soll.
  • Wenn Sie keine Werte auf dem Bildschirm wiedergeben möchten, weisen Sie sie einfach normal zu, z. B. myvar="$(config_get myvar)";. Wenn Sie sie auf dem Bildschirm drucken möchten, empfehle ich, printf zu verwenden, um vor echokompatiblen Zeichenfolgen in der Benutzerkonfiguration absolut sicher zu sein. Echo ist jedoch in Ordnung, wenn die vom Benutzer bereitgestellte Variable nicht vorhanden ist das erste Zeichen der Zeichenfolge, die Sie wiedergeben, da dies die einzige Situation ist, in der „Flags“ interpretiert werden könnten, sodass so etwas wie echo "foo: $(config_get myvar)"; sicher ist, da “ foo „beginnt“ nicht mit einem Bindestrich und teilt Echo daher mit, dass der Rest der Zeichenfolge auch keine „Flags“ dafür sind .:-)

Kommentare

  • @ user2993656 Vielen Dank, dass Sie festgestellt haben, dass mein ursprünglicher Code immer noch meinen privaten Konfigurationsdateinamen (environment.cfg) anstelle des richtigen enthält. Wie für die “ echo -n “ Bearbeitung, die Sie vorgenommen haben, hängt von der verwendeten Shell ab. Unter Mac / Linux Bash “ echo -n “ bedeutet “ Echo ohne Zeilenumbruch „, was ich getan habe, um nachlaufende Zeilenumbrüche zu vermeiden. Aber es scheint genauso zu funktionieren, also danke für die Änderungen!
  • Eigentlich habe ich es einfach durchgeschrieben, um printf anstelle von echo zu verwenden, was sicherstellt, dass wir ‚ beseitigt das Risiko einer Fehlinterpretation von “ Flags “ in den Konfigurationswerten.
  • ch mag diese Version wirklich. Ich habe die config.cfg.defaults gelöscht, anstatt sie zum Zeitpunkt des Aufrufs von $(config_get var_name "default_value") zu definieren. tritarget.org/static/…

  • Ebenso – das ist großartig.

Antwort

Die häufigste, effizienteste und korrekteste Methode ist die Verwendung von source oder . als Kurzform. Zum Beispiel:

source /home/myuser/test/config 

oder

. /home/myuser/test/config 

Beachten Sie jedoch Folgendes Sicherheitsprobleme, die durch die Verwendung einer zusätzlichen externen Konfigurationsdatei entstehen können, da zusätzlicher Code eingefügt werden kann. Weitere Informationen, einschließlich Informationen zum Erkennen und Beheben dieses Problems, finden Sie im Abschnitt „Sichern“ unter http://wiki.bash-hackers.org/howto/conffile#secure_it

Kommentare

  • Ich hatte große Hoffnungen auf diesen Artikel (tauchte auch in meinen Suchergebnissen auf), aber der Autor ‚ Der Vorschlag, Regex zum Herausfiltern von Schadcode zu verwenden, ist eine sinnlose Übung.
  • Die Prozedur mit Punkt erfordert einen absoluten Pfad?Mit dem relativen funktioniert ‚ nicht

Antwort

Ich verwende dies in meinen Skripten:

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 } 

Sollte jede Zeichenkombination unterstützen, außer dass Schlüssel nicht = in ihnen, da das das Trennzeichen ist. Alles andere funktioniert.

% 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 

Dies ist außerdem völlig sicher, da source oder eval.

Kommentare

  • Ist dies nicht ‚ Um zuerst die Konfigurationsdatei zu erstellen, wenn sie nicht ‚ existiert? ‚ t, aber touch -a "${path}" wird natürlich sicherstellen, dass es existiert, auch ohne seine mtime leichtfertig zu aktualisieren.

Antwort

Dies ist prägnant und sicher:

# 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`) 

Die -i stellt sicher, dass Sie nur die Variablen von

Update: Ein Beispiel für die Sicherheit ist, dass

env -i "touch evil1 foo=omg boo=$(touch evil2)" 

keine berührten Dateien erzeugt. Auf dem Mac getestet mit bash, dh mit bsd env.

Kommentare

  • Sehen Sie nur, wie böse1- und böse2-Dateien erstellt werden, wenn Sie dies auf comm setzen on.vars „ `touch evil1 foo = omg boo = $ (touch evil2)` „
  • @pihentagy Für mich erzeugt das Folgende keine berührten Dateien env -i 'touch evil1 foo=omg boo=$(touch evil2)' . Läuft auf einem Mac.
  • zwar, kann aber nicht auf foo zugreifen. Ich ‚ habe versucht env -i ... myscript.sh und in diesem Skript ist foo nicht definiert. Wenn Sie jedoch “ Müll “ entfernen, funktioniert dies. Also danke fürs Erklären. : +1:

Antwort

Die meisten Benutzer, obwohl nicht viele Container, haben bereits die git binär. Warum also nicht git config für die Verwaltung der Anwendungskonfiguration unter Verwendung einer dedizierten, nicht konfliktreichen Konfigurationsdatei wie in den folgenden Beispielen?

# 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 

Weitere Befehle finden Sie im Handbuch Seite. Es ist ratsam, zunächst sicherzustellen, dass die Konfigurationsdatei vorhanden ist:

touch -a ~/.myapp 

Antwort

Dieser scheint sicher und kurz. Fühlen Sie sich frei, dies rücksichtslos zu knacken. Ich möchte einen besseren Weg kennen.

TL; DR;

while read LINE; do declare "$LINE"; done < evil.conf 

Ich verwende bash 4.3.48.

Es ist auch kompatibel mit bash --posix. Test siehe unten.

Aber sh unterstützt es nicht wegen declare.


Grundlegender Test für diejenigen, die Beweise wollen

Erstellen Sie die Datei 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"  

Laden Sie die Konfiguration mit dem Snippet

 while read LINE; do declare "$LINE"; done < evil.conf  

Ausgabe (siehe Desinfektionsmittel in 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 

Lassen Sie uns jetzt die Werte überprüfen

 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" 

Überprüfen Sie die Nebenwirkungen (keine Nebenwirkungen):

 ls evil evil2  
ls: cannot access "evil": No such file or directory ls: cannot access "evil2": No such file or directory 

Anhang. 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 

Antwort

Für mein Szenario source oder . w genauso gut, aber ich wollte lokale Umgebungsvariablen (dh FOO=bar myscript.sh) unterstützen, die Vorrang vor konfigurierten Variablen haben. Ich wollte auch, dass die Konfigurationsdatei vom Benutzer bearbeitet werden kann und für jemanden, der Konfigurationsdateien verwendet, komfortabel ist, und dass sie so klein / einfach wie möglich gehalten wird, um nicht vom Hauptschwerpunkt meines sehr kleinen Skripts abzulenken.

Folgendes habe ich mir ausgedacht:

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) 

Im Wesentlichen – es sucht nach Variablendefinitionen (ohne sehr flexibel in Bezug auf Leerzeichen zu sein) und schreibt diese Zeilen neu, so dass die Der Wert wird in einen Standardwert für diese Variable konvertiert, und die Variable wird nicht geändert, wenn sie gefunden wird, wie die Variable XDG_CONFIG_HOME oben. Es bezieht diese geänderte Version der Konfigurationsdatei und fährt fort.

Zukünftige Arbeiten könnten das Skript sed robuster machen und Zeilen herausfiltern, die seltsam oder nicht gut aussehen. “ t Definitionen usw. werden am Ende der Zeilenkommentare nicht unterbrochen – aber das ist für mich vorerst gut genug.

Antwort

Sie können dies tun:

#!/bin/bash name="mohsen" age=35 cat > /home/myuser/test/config << EOF Name=$name Age=$age EOF 

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.