Was ist der Unterschied zwischen den Bash-Operatoren [[vs [vs (vs ((?


245

Ich bin ein bisschen verwirrt darüber, was diese Operatoren anders machen, wenn sie in Bash verwendet werden (Klammern, doppelte Klammern, Klammern und doppelte Klammern).

[[ , [ , ( , ((

Ich habe Leute gesehen, die sie benutzt haben, wenn Aussagen wie diese:

if [[condition]]

if [condition]

if ((condition))

if (condition)



3
@ Cuonglm Ironic, weil dieser Link diese Frage als erstes Ergebnis liefert . Paradox!
Insane

5
Ich nehme an, das Lesen der Dokumentation ist keine Option.
Leichtigkeitsrennen im Orbit

34
Klammern und Klammern sind in der Dokumentation nicht so einfach zu suchen, und das ist alles, was Sie haben, wenn Sie die Namen dieser Funktionen nicht kennen.
Ilkkachu

Antworten:


257

Eine ifAussage sieht normalerweise so aus

if commands1
then
   commands2
else
   commands3
fi

Die thenKlausel wird ausgeführt, wenn der Exit-Code von commands1Null ist. Wenn der Exit-Code ungleich Null ist, wird die elseKlausel ausgeführt. commands1kann einfach oder komplex sein. Es kann zum Beispiel eine Folge von einer oder mehreren Rohrleitungen von einem der Operatoren getrennt sind ;, &, &&, oder ||. Die ifunten aufgeführten Bedingungen sind nur Sonderfälle von commands1:

  1. if [ condition ]

    Dies ist der traditionelle Shell - testBefehl. Es ist auf allen POSIX-Shells verfügbar. Der Testbefehl setzt einen Exit-Code und die ifAnweisung verhält sich entsprechend. Typische Tests sind, ob eine Datei existiert oder eine Zahl mit einer anderen übereinstimmt.

  2. if [[ condition ]]

    Dies ist eine neue aktualisierte Variante testvon ksh , die auch bash und zsh unterstützt. Dieser testBefehl setzt auch einen Exit-Code und die ifAnweisung verhält sich entsprechend. Unter den erweiterten Funktionen kann getestet werden, ob eine Zeichenfolge mit einem regulären Ausdruck übereinstimmt.

  3. if ((condition))

    Eine weitere ksh- Erweiterung, die auch bash und zsh unterstützt. Dies führt eine Arithmetik durch. Als Ergebnis der Arithmetik wird ein Exit-Code gesetzt und die ifAnweisung entsprechend ausgeführt. Es gibt einen Exit-Code von Null (true) zurück, wenn das Ergebnis der arithmetischen Berechnung ungleich Null ist. Ebenso [[...]]ist dieses Formular nicht POSIX und daher nicht portierbar.

  4. if (command)

    Dadurch wird der Befehl in einer Subshell ausgeführt. Nach Abschluss des Befehls wird ein Beendigungscode festgelegt und die ifAnweisung entsprechend ausgeführt.

    Ein typischer Grund für ein Sub - Shell wie dies mit zu Nebenwirkungen zu begrenzen , commandwenn commandVariablenzuweisungen oder andere Änderungen erforderlich , um die Umgebung des Shell. Solche Änderungen bleiben nach Abschluss der Subshell nicht erhalten.

  5. if command

    Befehl wird ausgeführt und die ifAnweisung verhält sich entsprechend ihrem Exit-Code.


24
Vielen Dank, dass Sie die 5. Option hinzugefügt haben. Dies ist der Schlüssel zum Verständnis, wie dies tatsächlich funktioniert und überraschend wenig genutzt wird.
Küken

4
Beachten Sie, dass [es sich tatsächlich um eine Binärdatei handelt, nicht um einen internen Befehl oder ein internes Symbol. Lebt in der Regel in /bin.
Julien R.

8
@JulienR. Ist eigentlich so wie es [ist eingebaut test. Aus Kompatibilitätsgründen sind Binärversionen verfügbar. Check out help [und help test.
OldTimer

4
Erwähnenswert ist, dass while ((nicht POSIX ist, $((dh arithmetische Erweiterung ist und es leicht ist, sie zu verwechseln. Oft besteht eine [ $((2+2)) -eq 4 ]Problemumgehung darin, in bedingten Anweisungen so etwas wie die Verwendung von Arithmetik zu verwenden
Sergiy Kolodyazhnyy 30.11.17

1
Ich wünschte, ich könnte diese Antwort mehr als einmal hochstimmen. Perfekte Erklärung.
Anthony Gatlin vor

77
  • (…)Klammern kennzeichnen eine Unterschale . Was in ihnen steckt, ist kein Ausdruck wie in vielen anderen Sprachen. Es ist eine Liste von Befehlen (genau wie außerhalb von Klammern). Diese Befehle werden in einem separaten Unterprozess ausgeführt, sodass alle innerhalb der Klammern ausgeführten Umleitungen, Zuweisungen usw. keine Auswirkungen außerhalb der Klammern haben.
    • Mit einem führenden Dollarzeichen $(…)ist eine Befehlsersetzung : In den Klammern steht ein Befehl, und die Ausgabe des Befehls wird als Teil der Befehlszeile verwendet (nach zusätzlichen Erweiterungen, es sei denn, die Ersetzung erfolgt zwischen doppelten Anführungszeichen, aber das ist eine andere Geschichte ). .
  • { … }Klammern ähneln Klammern, da sie Befehle gruppieren, aber sie beeinflussen nur das Parsen, nicht das Gruppieren. Das Programm gibt x=2; { x=4; }; echo $x4 aus, während x=2; (x=4); echo $x2 ausgegeben wird. (Auch geschweifte Klammern als Schlüsselwörter müssen begrenzt und an der Befehlsposition gefunden werden (daher das Leerzeichen nach {und ;vor }), während Klammern dies nicht tun. Das ist nur eine Syntax-Eigenheit.)
    • Bei einem führenden Dollarzeichen ${VAR}handelt es sich um eine Parametererweiterung , die mit möglichen zusätzlichen Transformationen auf den Wert einer Variablen erweitert wird. Die ksh93Shell unterstützt auch eine ${ cmd;}Form der Befehlsersetzung, die keine Subshell erzeugt.
  • ((…))Doppelte Klammern umgeben eine arithmetische Anweisung , dh eine Berechnung mit ganzen Zahlen, mit einer Syntax, die anderen Programmiersprachen ähnelt. Diese Syntax wird hauptsächlich für Zuweisungen und in Bedingungen verwendet. Dies gibt es nur in ksh / bash / zsh, nicht in plain sh.
    • Dieselbe Syntax wird in arithmetischen Ausdrücken verwendet $((…)), die auf den ganzzahligen Wert des Ausdrucks erweitert werden.
  • [ … ]einzelne Klammern umgeben bedingte Ausdrücke . Bedingte Ausdrücke basieren hauptsächlich auf Operatoren , um -n "$variable"zu testen, ob eine Variable leer ist und -e "$file"ob eine Datei vorhanden ist. Beachten Sie, dass Sie ein Leerzeichen um jeden Operator (z. B. [ "$x" = "$y" ]nicht [ "$x"="$y" ]) und ein Leerzeichen oder ein Zeichen wie ;innerhalb und außerhalb der Klammern (z . B. [ -n "$foo" ]nicht [-n "$foo"]) benötigen .
  • [[ … ]]Doppelte Klammern sind eine alternative Form bedingter Ausdrücke in ksh / bash / zsh mit einigen zusätzlichen Funktionen. Sie können beispielsweise schreiben [[ -L $file && -f $file ]], um zu testen, ob eine Datei eine symbolische Verknüpfung zu einer regulären Datei ist, während einzelne Klammern dies erfordern [ -L "$file" ] && [ -f "$file" ]. Siehe Warum funktioniert die Parametererweiterung mit Leerzeichen ohne Anführungszeichen in doppelten Klammern [[aber nicht in einfachen Klammern [? für mehr zu diesem Thema.

In der Shell ist jeder Befehl ein bedingter Befehl: Jeder Befehl hat einen Rückgabestatus, der entweder 0 ist, um Erfolg anzuzeigen, oder eine Ganzzahl zwischen 1 und 255 (und möglicherweise mehr in einigen Shells), um einen Fehler anzuzeigen. Der [ … ]Befehl (oder die [[ … ]]Syntaxform) ist ein bestimmter Befehl, der auch geschrieben werden kann test …und erfolgreich ist, wenn eine Datei vorhanden ist, wenn eine Zeichenfolge nicht leer ist oder wenn eine Zahl kleiner als eine andere ist usw. Die ((…))Syntaxform ist erfolgreich, wenn eine Zahl vorhanden ist ist ungleich Null. Hier einige Beispiele für Bedingungen in einem Shell-Skript:

  • Testen Sie, ob myfiledie Zeichenfolge enthalten ist hello:

    if grep -q hello myfile; then 
  • Wenn mydires sich um ein Verzeichnis handelt, wechseln Sie in dieses und erledigen Sie Folgendes:

    if cd mydir; then
      echo "Creating mydir/myfile"
      echo 'some content' >myfile
    else
      echo >&2 "Fatal error. This script requires mydir to exist."
    fi
  • Testen Sie, ob sich myfileim aktuellen Verzeichnis eine Datei befindet :

    if [ -e myfile ]; then 
  • Das gleiche, aber auch herabhängende symbolische Links:

    if [ -e myfile ] || [ -L myfile ]; then 
  • Testen Sie, ob der Wert von x(der als numerisch angenommen wird) mindestens 2 ist, tragbar:

    if [ "$x" -ge 2 ]; then 
  • Testen Sie xin bash / ksh / zsh, ob der Wert von (der als numerisch angenommen wird) mindestens 2 beträgt:

    if ((x >= 2)); then 

Beachten Sie, dass einzelne Klammern -aanstelle von unterstützen &&, so dass man schreiben kann: [ -L $file -a -f $file ]Dies ist die gleiche Anzahl von Zeichen in den Klammern ohne das Extra [und ]...
Alexis Wilke

6
@AlexisWilke Die Operatoren -aund -osind problematisch, weil sie zu falschen Parses führen können, wenn einige der beteiligten Operanden wie Operatoren aussehen. Deshalb erwähne ich sie nicht: Sie haben keinen Vorteil und funktionieren nicht immer. Und schreiben Sie niemals ohne guten Grund nicht zitierte Variablenerweiterungen: [[ -L $file -a -f $file ]]ist in Ordnung, aber mit einzelnen Klammern [ -L "$file" -a -f "$file" ](was zB in Ordnung ist, wenn $fileimmer mit /oder begonnen wird ./).
Gilles

Beachten Sie, dass es ist [[ -L $file && -f $file ]](nein -amit der [[...]]Variante).
Stéphane Chazelas

18

Aus der Bash-Dokumentation :

(list)list wird in einer Subshell-Umgebung ausgeführt (siehe COMMAND EXECUTION ENVIRONMENT unten). Variablenzuweisungen und integrierte Befehle, die sich auf die Umgebung der Shell auswirken, bleiben nach Abschluss des Befehls nicht wirksam. Der Rückgabestatus ist der Exit-Status der Liste.

Mit anderen Worten, Sie stellen sicher, dass das, was auch immer in 'list' (wie a cd) passiert, keine Auswirkung außerhalb von (und hat ). Die einzige Sache , die undicht wird , ist der Exit - Code des letzten Befehls oder die mit set -edem ersten Befehl, der einen Fehler (außer ein paar wie erzeugt if, whileusw.)

((expression))Der Ausdruck wird gemäß den nachstehend unter ARITHMETISCHE BEWERTUNG beschriebenen Regeln ausgewertet. Wenn der Wert des Ausdrucks nicht Null ist, ist der Rückgabestatus 0; Andernfalls lautet der Rückgabestatus 1. Dies ist genau gleichbedeutend mit let "expression".

Dies ist eine Bash-Erweiterung, mit der Sie rechnen können. Dies ähnelt in gewisser Weise der Verwendung exprohne alle Einschränkungen von expr(z. B. überall Leerzeichen, Flucht *usw.).

[[ expression ]]Gibt einen Status von 0 oder 1 zurück, abhängig von der Auswertung des bedingten Ausdrucks expression. Ausdrücke setzen sich aus den im Folgenden unter BEDINGTE AUSDRÜCKE beschriebenen Primären zusammen. Wortteilung und Pfadnamenerweiterung werden nicht für die Wörter zwischen [[und]] ausgeführt. Tilde-Erweiterung, Parameter- und Variablenerweiterung, arithmetische Erweiterung, Befehlssubstitution, Prozessersetzung und Entfernen von Anführungszeichen werden ausgeführt. Bedingte Operatoren wie -f müssen nicht in Anführungszeichen gesetzt werden, um als primäre Operatoren erkannt zu werden.

Bei Verwendung mit [[sortieren die Operatoren <und> lexikografisch nach dem aktuellen Gebietsschema.

Dies bietet einen erweiterten Test zum Vergleichen von Zeichenfolgen, Zahlen und Dateien, der den testAngeboten ähnelt, jedoch leistungsfähiger ist.

[ expr ]Gibt einen Status von 0 (true) oder 1 (false) zurück, abhängig von der Auswertung des bedingten Ausdrucks expr. Jeder Operator und oper und muss ein separates Argument sein. Ausdrücke setzen sich aus den oben unter BEDINGTE AUSDRÜCKE beschriebenen Primären zusammen. test akzeptiert weder Optionen noch akzeptiert und ignoriert es ein Argument von -, um das Ende von Optionen zu kennzeichnen.

[...]

Dieser ruft test. Eigentlich war in alten Zeiten [eine symbolische Verbindung zu test. Es funktioniert genauso und Sie haben die gleichen Einschränkungen. Da eine Binärdatei den Namen kennt, mit dem sie gestartet wurde, kann das Testprogramm Parameter analysieren, bis es einen Parameter findet ]. Lustige Unix-Tricks.

Beachten Sie, dass im Fall bash, [und testsind integrierte Funktionen (wie in einem Kommentar erwähnt), aber so ziemlich die gleichen Einschränkungen gelten.


1
Obwohl testund [natürlich in Bash eingebaute Befehle sind, ist es wahrscheinlich, dass auch eine externe Binärdatei existiert.
Ilkkachu

1
Die externe Binärdatei für [ist testauf den meisten modernen Systemen keine symbolische Verknüpfung .
Random832

1
Irgendwie finde ich es amüsant, dass sie sich die Mühe machen, zwei separate Binärdateien zu erstellen, die beide genau das haben, was sie brauchen, anstatt sie nur zu kombinieren und ein paar Bedingungen hinzuzufügen. Obwohl es tatsächlich strings /usr/bin/testden Hilfetext enthält, weiß ich nicht, was ich sagen soll.
Ilkkachu

2
@ Random832 Ich verstehe Ihre Argumentation bezüglich der GNU-Gründe, um unerwartetes arg0-Verhalten zu vermeiden, aber bezüglich der POSIX-Anforderungen, wäre ich nicht so positiv. Obwohl der testBefehl nach dem Standard offensichtlich als eigenständiger dateibasierter Befehl existieren muss, gibt nichts in ihm an, dass seine [Variante auch auf diese Weise implementiert werden muss. Zum Beispiel stellt Solaris 11 keine [ausführbare Datei zur Verfügung, ist aber dennoch vollständig mit den POSIX-Standards kompatibel
Juli

2
(Ausgang 1) wirkt sich außerhalb der Klammern aus.
Alexander

14

[ vs [[

Diese Antwort deckt die [vs- [[Teilmenge der Frage ab.

Einige Unterschiede zu Bash 4.3.11:

  • POSIX vs Bash-Erweiterung:

  • Regelmäßiges Kommando gegen Magie

    • [ ist nur ein regulärer Befehl mit einem seltsamen Namen.

      ]Dies ist nur ein Argument [, das die Verwendung weiterer Argumente verhindert.

      Ubuntu 16.04 hat tatsächlich eine ausführbare Datei, /usr/bin/[die von coreutils zur Verfügung gestellt wird, aber die in Bash integrierte Version hat Vorrang.

      An der Art und Weise, wie Bash den Befehl analysiert, ändert sich nichts.

      Insbesondere werden durch <Umleitung &&und ||Verketten mehrerer Befehle ( )Unterschalen generiert, sofern diese nicht durch Escapezeichen geschützt werden \, und die Worterweiterung erfolgt wie gewohnt.

    • [[ X ]]ist ein einzelnes Konstrukt, das Xmagisch analysiert werden kann. <, &&, ||Und ()sind speziell, und einzelne Wörter aufgespalten Regeln behandelt unterschiedlich.

      Es gibt auch weitere Unterschiede wie =und =~.

      In Bashese: [ist ein integrierter Befehl und [[ein Schlüsselwort: https://askubuntu.com/questions/445749/was ist der Unterschied zwischen dem eingebauten Shell- und dem Shell - Schlüsselwort ?

  • <

  • && und ||

    • [[ a = a && b = b ]]: wahr, logisch und
    • [ a = a && b = b ]: Syntaxfehler, der &&als UND-Befehlstrennzeichen analysiert wirdcmd1 && cmd2
    • [ a = a -a b = b ]: äquivalent, aber von POSIX³ veraltet
    • [ a = a ] && [ b = b ]: POSIX und zuverlässiges Äquivalent
  • (

    • [[ (a = a || a = b) && a = b ]]: falsch
    • [ ( a = a ) ]: Syntaxfehler, ()wird als Subshell interpretiert
    • [ \( a = a -o a = b \) -a a = b ]: äquivalent, wird aber ()von POSIX nicht mehr unterstützt
    • { [ a = a ] || [ a = b ]; } && [ a = b ]POSIX-Äquivalent 5
  • Wortteilung und Generierung von Dateinamen bei Erweiterungen (split + glob)

    • x='a b'; [[ $x = 'a b' ]]: true, keine Anführungszeichen erforderlich
    • x='a b'; [ $x = 'a b' ]: Syntaxfehler, erweitert auf [ a b = 'a b' ]
    • x='*'; [ $x = 'a b' ]: Syntaxfehler, wenn sich mehr als eine Datei im aktuellen Verzeichnis befindet.
    • x='a b'; [ "$x" = 'a b' ]: POSIX-Äquivalent
  • =

    • [[ ab = a? ]]: true, weil es Pattern Matching macht ( * ? [sind magisch). Wird nicht global auf Dateien im aktuellen Verzeichnis erweitert.
    • [ ab = a? ]: a?glob erweitert. Dies kann je nach den Dateien im aktuellen Verzeichnis wahr oder falsch sein.
    • [ ab = a\? ]: false, keine Glob-Erweiterung
    • =und ==sind in beiden [und gleich [[, ist aber ==eine Bash-Erweiterung.
    • case ab in (a?) echo match; esac: POSIX-Äquivalent
    • [[ ab =~ 'ab?' ]]: false 4 , verliert Magie mit''
    • [[ ab? =~ 'ab?' ]]: wahr
  • =~

    • [[ ab =~ ab? ]]: true, POSIX- Übereinstimmung mit erweiterten regulären Ausdrücken , ?Glob-Erweiterung nicht möglich
    • [ a =~ a ]: Syntax-Fehler. Kein Bash-Äquivalent.
    • printf 'ab\n' | grep -Eq 'ab?': POSIX-Äquivalent (nur einzeilige Daten)
    • awk 'BEGIN{exit !(ARGV[1] ~ ARGV[2])}' ab 'ab?': POSIX-Äquivalent.

Empfehlung : immer verwenden [].

Für jedes [[ ]]Konstrukt, das ich gesehen habe, gibt es POSIX-Entsprechungen .

Wenn Sie Sie benutzen [[ ]]:

  • Portabilität verlieren
  • den Leser zwingen, die Feinheiten einer anderen Bash-Erweiterung zu lernen. [ist nur ein regulärer Befehl mit einem seltsamen Namen, es ist keine spezielle Semantik involviert.

¹ Inspiriert von dem entsprechenden [[...]]Konstrukt in der Korn-Shell

² schlägt jedoch für einige Werte von aoder b(wie +oder index) fehl und führt einen numerischen Vergleich durch, wenn aund bwie Dezimalzahlen aussehen. expr "x$a" '<' "x$b"funktioniert um beide.

³ und fällt auch für einige Werte von aoder bwie !oder aus (.

4 in Bash 3.2 und höher und vorausgesetzt, die Kompatibilität zu Bash 3.1 ist nicht aktiviert (wie bei BASH_COMPAT=3.1)

5 obwohl die Gruppierung (hier mit der {...;}Befehlsgruppe, statt der (...)eine unnötige Subshell ausgeführt werden würde) nicht erforderlich ist, da die Operatoren ||und die &&Shell (im Gegensatz zu den Operatoren ||und && [[...]]oder den Operatoren -o/ -a [) den gleichen Vorrang haben. Wäre [ a = a ] || [ a = b ] && [ a = b ]also gleichwertig.


@ StéphaneChazelas danke für die Info! Ich habe exprdie Antwort hinzugefügt . Der Begriff "Bash-Erweiterung" bedeutet nicht, dass Bash die erste Shell war, die eine Syntax hinzufügte. Das Erlernen von POSIX sh vs Bash reicht bereits aus, um mich in den Wahnsinn zu treiben.
Ciro Santilli

Sehen man testSie nach, ob Sie es versucht haben man [und sich verlaufen haben. Das erklärt die POSIX-Variante.
Jonathan Komar

13

Einige Beispiele:

Traditioneller Test:

foo="some thing"
# check if value of foo is not empty
if [ -n "$foo" ] ; then... 
if test -n "$foo" ; then... 

testund [sind Befehle wie alle anderen, daher wird die Variable in Wörter aufgeteilt, sofern sie nicht in Anführungszeichen steht.

Neuer Test

[[ ... ]] ist ein (neueres) spezielles Shell-Konstrukt, das ein bisschen anders funktioniert. Das offensichtlichste ist, dass es keine wortgeteilten Variablen gibt:

if [[ -n $foo ]] ; then... 

Einige Dokumentationen dazu [und [[hier .

Arithmetischer Test:

foo=12 bar=3
if (( $foo + $bar == 15 )) ; then ...  

"Normal" -Befehle:

Alle oben genannten Befehle verhalten sich wie normale Befehle und ifkönnen jeden Befehl annehmen:

# grep returns true if it finds something
if grep pattern file ; then ...

Mehrere Befehle:

Oder wir können mehrere Befehle verwenden. Durch das Einschließen einer Reihe von Befehlen werden ( ... )diese in der Subshell ausgeführt und eine temporäre Kopie des Shell-Status (Arbeitsverzeichnis, Variablen) erstellt. Wenn wir ein Programm vorübergehend in einem anderen Verzeichnis ausführen müssen:

# this will move to $somedir only for the duration of the subshell 
if ( cd $somedir ; some_test ) ; then ...

# while here, the rest of the script will see the new working
# directory, even after the test
if cd $somedir ; some_test ; then ...

1

Gruppierungsbefehle

Bash bietet zwei Möglichkeiten zum Gruppieren einer Liste von Befehlen, die als Einheit ausgeführt werden sollen.

( list )Wenn Sie eine Befehlsliste in Klammern setzen, wird eine Subshell-Umgebung erstellt und alle Befehle in der Liste in dieser Subshell ausgeführt. Da die Liste in einer Subshell ausgeführt wird, bleiben die Variablenzuweisungen nach Abschluss der Subshell nicht wirksam.

$ a=1; (a=2; echo "inside: a=$a"); echo "outside: a=$a"
inside: a=2
outside: a=1

{ list; }Wenn Sie eine Befehlsliste in geschweifte Klammern setzen, wird die Liste im aktuellen Shell-Kontext ausgeführt . Es wird keine Subshell erstellt. Die folgende Liste mit Semikolon (oder Zeilenvorschub) ist erforderlich. Quelle

${} Parameter expansion Ex:  ANIMAL=duck; echo One $ANIMAL, two ${ANIMAL}s
$() Command substitution Ex: result=$(COMMAND) 
$(()) Arithmetic expansion Ex: var=$(( 20 + 5 )) 

Bedingte Konstrukte

Einzelne Klammer dh []
Zum Vergleich ==, !=, <,und >und sollte verwendet werden und zum numerischen Vergleich eq, ne,ltund gtsollte verwendet werden.

Erweiterte Klammern dh[[]]

In allen obigen Beispielen haben wir nur einfache Klammern verwendet, um den bedingten Ausdruck einzuschließen, aber bash erlaubt doppelte Klammern, die als erweiterte Version der Syntax für einfache Klammern dienen.

Zum Vergleich ==, !=, <,und >im wahrsten Sinne des Wortes.

  • [ist ein Synonym für Testbefehl. Selbst wenn es in die Shell integriert ist, wird ein neuer Prozess erstellt.
  • [[ ist eine neue verbesserte Version davon, die ein Schlüsselwort ist, kein Programm.
  • [[wird verstanden unter Kornund Bash.

Quelle

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.