Kategorie:Bash/Expansionen: Unterschied zwischen den Versionen

Aus Foxwiki
Zeile 10: Zeile 10:
|}
|}


== Installation ==
== Syntax ==
== Syntax ==
=== Parameter ===
=== Parameter ===

Version vom 25. Juli 2022, 13:58 Uhr

topic kurze Beschreibung

Beschreibung

Priotität Expansionen Beschreibung
1 Bash:Klammerexpansion Brace expansion

Syntax

Parameter

Optionen

Umgebungsvariablen

Exit-Status

Konfiguration

Dateien

Anwendungen

Tildeexpansion

siehe Bash:Tildeexpansion

Parameterexpansion

siehe Bash:Parameterexpansion

Kommandosubstitution

Die Kommandosubstitution erlaubt das Ersetzen ihres Aufrufes durch ihre Ausgabe. Es existieren zwei Syntaxvarianten des Aufrufs:

$(Kommando)
`Kommando`

Die Bash führt das Kommando aus und ersetzt seinen Aufruf auf der Kommandozeile durch dessen Ausgabe, wobei abschließende Zeilenendezeichen entfernt wurden.

# ohne Kommandosubstitution 
find / -name "whatis" 2>/dev/null | ls -l | head -5
insgesamt 15888
-rw-r--r--    1 user    users    787067 Apr  1 09:02 Buch.tar.gz
drwxr-xr-x    4 user    users      4096 Jan 16 19:49 Dhtml
drwx------    5 user    users      4096 Apr 26 09:48 Desktop
drwxr-xr-x    4 user    users      4096 Apr 21 08:43 IGLinux

# mit Kommandosubstitution
ls -l $(find / -name "whatis" 2>/dev/null)
ls -l $(find / -name "whatis"  2>/dev/null)
-rw-r--r--    1 root     root     94414 Jun 13 18:34 /usr/X11R6/man/whatis
-rw-r--r--    1 root     root    792270 Jun 13 18:34 /usr/man/allman/whatis
-rw-r--r--    1 root     root    220874 Jun 13 18:34 /usr/man/whatis
-rw-r--r--    1 root     root         0 Jun 13 18:34 /usr/openwin/man/whatis

Eine solche Kommandosubstitution kann auch bei der Zuweisung an eine Variable angewendet werden:

AktuellPfad=$(pwd)
echo $AktuellerPfad
/home/user

Zwischen Backquote (Accent Grave) gesetzte Kommandos werden ausgeführt und das Ergebnis wird dann als Parameter übergeben (d. h. die Ausgabe des Kommandos landet als Parameter in der Kommandozeile). Dabei werden Zeilenwechsel zu Leerzeichen. Braucht dieses Kommando Parameter, tritt die normale Parameterersetzung in Kraft. Zum Beispiel

echo "Aktuelles Verzeichnis: `pwd`"

Weil die verschiedenen Quotes manchmal schwer zu unterscheiden sind, wurde bei der bash eine weitere Möglichkeit eingeführt. Statt in Backquotes wird die Kommandofolge in $(...) eingeschlossen., z. B.:

echo "Aktuelles Verzeichnis: $(pwd)"

Command substitution allows the output of a command to replace the command itself. Command substitution occurs when a command is enclosed like this:

$(command) or like this using backticks: `command`

Bash performs the expansion by executing COMMAND and replacing the command substitution with the standard output of the command, with any trailing newlines deleted.

Embedded newlines are not deleted, but they may be removed during word splitting.

echo `date`
Thu Feb 6 10:06:20 CET 2003

When the old-style backquoted (accent aigu) form of substitution is used, backslash retains its literal meaning except when followed by "$", "`", or "\".

The first backticks not preceded by a backslash terminates the command substitution.

When using the "$(COMMAND)" form, all characters between the parentheses make up the command; none are treated specially.

Command substitutions may be nested. To nest when using the backquoted form, escape the inner backticks with backslashes.

If the substitution appears within double quotes, word splitting and file name expansion are not performed on the results.

Prozesssubstitution

Die Ein- bzw. Ausgabe von Prozessen kann mittels der Prozesssubstitution mit einer FIFO-Datei verbunden werden.

Taucht ein Konstrukt der Art <(Liste) bzw. >(Liste) auf, werden die durch Liste benannten Kommandos in einer Subshell gestartet. Gleichzeitig wird die Ausgabe (>(...)) bzw. Eingabe (<(...)) der Kommandos mit einer automatisch erzeugten FIFO-Datei verbunden.

Auf der Kommandozeile erscheint nach erfolgter Substitution der Name der erzeugten FIFO-Datei.

ls <(echo "hello")
/dev/fd/63

Mit Hilfe der Prozesssubstitution könnte man den vi dazu bewegen, die Ausgaben eines Kommandos zu lesen:

vi <(ls /boot/vm*)
/boot/vmlinuz
/boot/vmlinuz.old
~

~
"/dev/fd/63" [fifo/socket] 2L, 32C
                                       1,1            All

Ein weiteres Beispiel dient zur Bestandsaufnahme laufender Prozesse:

diff <(ps ax) <(sleep 10; ps ax)
64d63
<  2129 pts/0    S      0:00 /bin/bash
67,68c66
<  2132 pts/0    R      0:00 ps ax
<  2133 pts/0    S      0:00 sleep 10
---
>  2134 pts/1    S      0:00 top
>  2135 pts/0    R      0:00 ps ax

Im Beispiel ist der Prozess top neu hinzugekommen, dass die Aufrufe der Kommandos ps und sleep erscheinen, war zu erwarten.

Und abschließend vergleichen wir die Inhalte zweier Archive:

diff <(tar tzf Buch1.tar.gz) <(tar tzf Buch.tar.gz)
325a326,328
> Images/tkinfo.gif
> Images/GlobaleVariable.gif
> Images/LokaleVariable.gif

Innerhalb der Klammern >(...), <(...) können Parameter- Kommando- sowie arithmetische Substitutionen benutzt werden.

Arithmetische Substitution

Die Bash ist kein Taschenrechner. Dennoch besitzt sie ein erstaunliches Potenzial an eingebauten Rechenoperationen, die -- nach Prioritäten geordnet -- nachfolgende Tabelle zusammenfasst:

VAR++ and VAR-- variable post-increment and post-decrement
++VAR and --VAR variable pre-increment and pre-decrement
+ - Einstelliger Operator (Vorzeichen)
! ~ Logische und bitweise Negation
** Exponentialfunktion
* / % Multiplikation, Division und Modulo-Operator
+ - Addition, Subtraktion
<< >> Bitweise Links-/Rechtsverschiebung
<= >= < > Vergleiche
== != Gleichheit und Ungleichheit
& Bitweises UND
^ Bitweises Exclusive ODER
| Bitweises ODER
&& Logisches UND
Logisches ODER
expr ? expr : expr Bedingte Zuweisung
=, *=, /=, %=, +=, -= <<=, >>=, &=, ^=, |= Zuweisungen
, separator between expressions

Als Operanden sind Konstanten und Shellvariablen (deren Inhalt als long integer betrachtet wird) erlaubt. Beginnt eine Konstante mit "0", dann wird sie als oktale Zahl verstanden; steht "0x" am Anfang, handelt es sich um eine hexadezimale Konstante.

Konstanten können zu jeder Basis zwischen 2 und 64 angegeben werden, so kann die Zahl 63 u.a. wie folgt dargestellt werden: * Zur Basis 10: 10#63

  • Zur Basis 8: 8#77
  • Zur Basis 16: 16#3f

Die so genannte arithmetische Substitution ist der gebräuchliche Weg, um Berechnungen durchzuführen: * Bash Versionen <2: Der zu berechnende Ausdruck wird in eckigen Klammern geschrieben: $[...]

  • Bash ab Version 2: Der zu berechnende Ausdruck wird in doppelte runde Klammern geschrieben: $((...)) (die alte Syntax wird weiterhin unterstützt)

Arithmetic expansion allows the evaluation of an arithmetic expression and the substitution of the result. The format for arithmetic expansion is:

$(( EXPRESSION ))

The expression is treated as if it were within double quotes, but a double quote inside the parentheses is not treated specially.

All tokens in the expression undergo parameter expansion, command substitution, and quote removal. Arithmetic substitutions may be nested.

Evaluation of arithmetic expressions is done in fixed-width integers with no check for overflow - although division by zero is trapped and recognized as an error.

The operators are roughly the same as in the C programming language. In order of decreasing precedence, the list looks like this:

Shell variables are allowed as operands; parameter expansion is performed before the expression is evaluated.

Within an expression, shell variables may also be referenced by name without using the parameter expansion syntax.

The value of a variable is evaluated as an arithmetic expression when it is referenced. A shell variable need not have its integer attribute turned on to be used in an expression.

Constants with a leading 0 (zero) are interpreted as octal numbers. A leading "0x" or "0X" denotes hexadecimal. Otherwise, numbers take the form "[BASE'#']N", where "BASE" is a decimal number between 2 and 64 representing the arithmetic base, and N is a number in that base.

If "BASE'#'" is omitted, then base 10 is used. The digits greater than 9 are represented by the lowercase letters, the uppercase letters, "@", and "_", in that order.

If "BASE" is less than or equal to 36, lowercase and uppercase letters may be used interchangably to represent numbers between 10 and 35.

Operators are evaluated in order of precedence. Sub-expressions in parentheses are evaluated first and may override the precedence rules above.

Wherever possible, Bash users should try to use the syntax with square brackets:

$[ EXPRESSION ] 

However, this will only calculate the result of EXPRESSION, and do no tests:

echo $[365*24]
8760

Übungen

b=5; b=$((b+1)); echo $b

6

a=$((b+=10)); echo $a

16

echo $((a>b?1:0))

1

echo $((8#17**2))

225

echo $((017**2))

225

echo $((-0x64*3#11%6))

-4

echo $((4<<1))

8

Wird als Operand eine Variable benutzt, so wird versucht, deren Inhalt in eine Ganzzahl zu konvertieren. Enthält die Variable keine Zahl, wird der Inhalt zu "0" konvertiert:

b="Ist b keine Zahl, wird b zu 0 konvertiert"

echo $b
Ist b keine Zahl, wird b zu 0 konvertiert

b=$(($b+1)); echo $b

1

Funktionen

Eine Funktion ist ein Name für ein Kommando oder für eine Gruppe von Kommandos.

Funktionen werden vorrangig in Shellskripten verwendet, um wiederkehrende Kommandosequenzen nicht ständig neu schreiben zu müssen.

Ein großer Vorteil von Funktionen ist, dass sich diese in einer Datei speichern lassen und diese Datei von anderen Skripten geladen werden kann.

Eine Funktion wird wie folgt definiert:

[function] Funktionsname() { Kommando; [Kommando;] }

Bei der Verwendung von Funktionen sind einige Regeln zu befolgen: * Deckt sich der Name der Funktion mit einem builtin-Kommando, wird immer die Funktion ausgeführt und niemals das Kommando. Ebenso verdeckt ein Funktionsname ein gleichnamiges Kommando:

type test
test is a shell builtin
test(){ echo "ich bin eine Funktion"; }
type test
test is a function
test ()
{
echo "ich bin eine Funktion"
}
unset test* Die Funktion muss vor ihrer Verwendung definiert sein. 
  • Eine Funktion läuft in der aktuellen Umgebung, d.h. alle Variablen der Umgebung sind sichtbar und alle Variablen, die in der Funktion definiert wurden, sind auch außerhalb sichtbar:
func(){ var_in_func=xxx; }
func
echo $var_in_func
xxx* Wird eine Funktion mittels »exit« verlassen, wird auch der rufende Prozess beendet:
func(){ exit; }
func
login:* Der Rückgabewert einer Funktion ist der Rückgabewert des letzten in ihr gestarteten Kommandos: 
func(){ grep -q foo /etc/passwd; echo $?; }
func
1
echo $?
0
func(){ return 255; }
func
echo $?
255* Funktionen sind nur in der Shell ihrer Definition bekannt: 
func(){ echo "lokal"; }
bash
func
bash: func: command not found
exit
func
lokal

Einer Funktion können Parameter als Argumente übergeben werden:

Aufruf: Funktionsname [Arg1] [Arg2]...

Innerhalb einer Funktion kann auf die Parameter mittels der Positionsparameter $1..$9 zugegriffen werden. Als Beispiel dient eine Funktion "square", die die als Argument übergebene Zahl quadriert:

square() { test -z $1 && return 1; expr $1 \* $1; }
square
square 18
324

Erklärung

Das builtin-Kommando test liefert den Status "0", falls die Variable "$1" leer ist (kein Argument).

In diesem Fall wird "return 1" ausgeführt und der Funktionsaufruf beendet. expr berechnet den Ausdruck und schreibt das Ergebnis auf die Standardausgabe.

Wortzerlegung

In diesem Schritt wird die Kommandozeile in sogenannte Token unterteilt. Welche Zeichen als Separatoren verwendet werden, verrät die Variable $IFS (Internal Field Separator).

Ist diese nicht gesetzt, gelten die schon erwähnten Whitespaces als Begrenzer, sofern sie nicht innerhalb von (Doppel) Anführungsstrichen stehen oder durch den Backslash "maskiert" wurden.

The shell scans the results of parameter expansion, command substitution, and arithmetic expansion that did not occur within double quotes for word splitting.

The shell treats each character of $IFS as a delimiter, and splits the results of the other expansions into words on these characters.

If IFS is unset, or its value is exactly "'<space><tab><newline>'", the default, then any sequence of IFS characters serves to delimit words.

If IFS has a value other than the default, then sequences of the whitespace characters "space" and "Tab" are ignored at the beginning and end of the word, as long as the whitespace character is in the value of IFS (an IFS whitespace character).

Any character in IFS that is not IFS whitespace, along with any adjacent IF whitespace characters, delimits a field.

A sequence of IFS whitespace characters is also treated as a delimiter. If the value of IFS is null, no word splitting occurs.

Explicit null arguments ("""" or "") are retained. Unquoted implicit null arguments, resulting from the expansion of parameters that have no values, are removed. If a parameter with no value is expanded within double quotes, a null argument results and is retained.

[[Image:Grafik29.png]] Expansion and word splitting
If no expansion occurs, no splitting is performed.

Expansion von Dateinamen

After word splitting, unless the -f option has been set , Bash scans each word for the characters "*", "?", and "[".

If one of these characters appears, then the word is regarded as a PATTERN, and replaced with an alphabetically sorted list of file names matching the pattern.

If no matching file names are found, and the shell option nullglob is disabled, the word is left unchanged.

If the nullglob option is set, and no matches are found, the word is removed.

If the shell option nocaseglob is enabled, the match is performed without regard to the case of alphabetic characters.

When a pattern is used for file name generation, the character "." at the start of a file name or immediately following a slash must be matched explicitly, unless the shell option dotglob is set.

When matching a file name, the slash character must always be matched explicitly. In other cases, the "." character is not treated specially.

The GLOBIGNORE shell variable may be used to restrict the set of file names matching a pattern.

If GLOBIGNORE is set, each matching file name that also matches one of the patterns in GLOBIGNORE is removed from the list of matches.

The file names . and .. are always ignored, even when GLOBIGNORE is set.

However, setting GLOBIGNORE has the effect of enabling the dotglob shell option, so all other file names beginning with a "." will match.

To get the old behavior of ignoring file names beginning with a ".", make ".*" one of the patterns in GLOBIGNORE. The dotglob option is disabled when GLOBIGNORE is unset.

Metazeichen und Globbing

Damit man beim Angeben von z. B. Dateinamen nicht alle Namen eintippen muss, sondern die Dateien auch alle oder nach bestimmten Kriterien auswählen kann, gibt es Metazeichen (Jokerzeichen, Wildcards).

Metazeichen sind Zeichen mit erweiterter Bedeutung

Expansion von Dateinamen

Im Gegensatz zu anderen Systemen (z. B. Windows) werden Metazeichen von der Shell durch alle passenden Dateinamen ersetzt, bevor sie die Befehlszeile ausführt. Dies führt zu einem für Windows-Anwender ungewohntem Verhalten.

Metazeichen werden von der Shell ersetzt

Vorteile* liegt darin, dass, dass nahezu jedes UNIX-Kommando als Dateiangabe eine beliebige Menge von Dateien als Parameter haben kann.

  • So sind in Programmen keine Systemaufrufe nötig, die auf die Verzeichnisinformation zugreifen; es wird lediglich eine Schleife benötigt, welche die einzelnen Dateien nacheinander bearbeitet.

Praktisches Globbing

Ganz schlecht

chmod a+r witzig1.jpg
chmod a+r witzig2.jpg
chmod a+r witzig3.jpg

Besser

chmod a+r witzig1.jpg witzig2.jpg witzig3.jpg

Super

chmod a+r *.jpg

Es gibt folgende Metazeichen

* Der Stern steht für eine beliebige Zeichenfolge - oder für überhaupt kein Zeichen.

"ab*" steht für alle Dateinamen, die mit "ab" anfangen, auch für "ab" selbst ("ab", "abc", "abcd", "abxyz", usw.).

? Das Fragezeichen steht für genau ein beliebiges Zeichen. Zum Beispiel

"?bc" steht für alle Dateinamen mit 3 Zeichen, die auf "bc" enden ("abc", "bbc", "1bc", "vbc", "xbc", usw.), nicht jedoch für "bc".

[...] Im einfachsten Fall steht es für genau ein Zeichen aus der Menge (bspw. "[aeiou]" für einen Vokal).

Diese Angabe kann negiert werden ("alle außer diese Zeichen"), indem das erste Zeichen nach [ ein ! oder ^ ist ("[!abc]" bzw. "[^abc]").

Anstatt einzelne Zeichen aufzuzählen, lassen sich Bereiche angeben ("[a-z]" meint alle Kleinbuchstaben).

Wichtig!

Der * ist ein gefährliches Zeichen, Tippfehler könne zum Fiasko führen, wenn aus Versehen ein Leerzeichen zuviel getippt wird.

rm a* löscht beispielsweise alle Dateien, die mit "a" anfangen.
rm a * löscht dagegen erst die Datei "a" und dann alle Dateien im Verzeichnis. 

Zeichenbereiche

Außer grep und Regulären Ausdrücken, gibt es eine gute Auswahl an pattern matching, dass die Shell direkt durchführen kann, ohne dass dafür externe Programme eingesetzt werden müssen.

Bekannt sind sicherlich das Sternchen (asterisk) „*“ und das Fragezeichen (question mark) „?“, die jede Zeichenkette oder jeden Buchstaben filtern. Um diese Sonderzeichen als Literale zu filtern müssen sie maskiert werden:

touch "*"
ls "*"
*

But you can also use the square braces to match any enclosed character or range of characters, if pairs of characters are separated by a hyphen. An example:

ls -ld [a-cx-z]*
drwxr-xr-x    2 dirkwagner    dirkwagner          4096 Jul 20  2002 app-defaults/
drwxrwxr-x    4 dirkwagner    dirkwagner          4096 May 25  2002 arabic/
drwxrwxr-x    2 dirkwagner    dirkwagner          4096 Mar  4 18:30 bin/
drwxr-xr-x    7 dirkwagner    dirkwagner          4096 Sep  2  2001 crossover/
drwxrwxr-x    3 dirkwagner    dirkwagner          4096 Mar 22  2002 xml/

This lists all files in dirkwagner's home directory, starting with "a", "b", "c", "x", "y" or "z".

If the first character within the braces is "!" or "^", any character not enclosed will be matched.

To match the dash ("-"), include it as the first or last character in the set.

The sorting depends on the current locale and of the value of the LC_COLLATE variable, if it is set.

Mind that other locales might interpret "[a-cx-z]" as "[aBbCcXxYyZz]" if sorting is done in dictionary order.

If you want to be sure to have the traditional interpretation of ranges, force this behavior by setting LC_COLLATE or LC_ALL to "C".

Nullglob

Ohne Match wird das Argument unverändert übernommen. Dieses Verhalten lässt sich mit der Shelloption nullglob beeinflussen.

Match - gefundene Dateien werden übernommen

echo a1*.jpg
a1.jpg a10.jpg a11.jpg

Kein Match - Argument bleibt unverändert

echo b1*.jpg
b1*.jpg

Dotfiles

Der Punkt am Anfang von Dateinamen stellt eine Ausnahme dar, er muss explizit angegeben werden (wegen der Verzeichnisreferenzen "." bzw. ".." und der Tatsache, dass Dateien, die mit einem Punkt beginnen, normalerweise nicht angezeigt werden). * Dateien, die mit "." beginnen sind versteckt

  • Shell Globbing ignoriert die "Dotfiles"
  • Punkt muss explizit genannt werden
echo .*
. .. .bash_history .bash_profile .bashrc

Globbing und Brace Expansion

Brace Expansion hat starke Ähnlichkeit mit Globbing doch die angegebenen Dateinamen müssen nicht existieren.

ls 
a1.jpg a10.jpg a11.jpg a2.jpg a3.jpg a4.jpg

"{name1,name2}" - Alternativen aufzählen

echo a{2,10}.jpg
a2.jpg a10.jpg

Zeichenklassen

Zeichenklassen können mit folgender Syntax in eckigen Klammen angegeben [:CLASS:]. Dabei wird CLASS nach POSIX-Standard angegeben und kann folgende Werte annehmen:

"alnum", "alpha", "ascii", "blank", "cntrl", "digit", "graph", "lower", "print", "punct", "space", "upper", "word" or "xdigit"

ls -ld digit:*
drwxrwxr-x    2 dirkwagner   dirkwagner           4096 Apr 20 13:45 2/
ls -ld upper:*
drwxrwxr--    3 dirkwagner   dirkwagner           4096 Sep 30  2001 Nautilus/
drwxrwxr-x    4 dirkwagner   dirkwagner           4096 Jul 11  2002 OpenOffice.org1.0/
-rw-rw-r--    1 dirkwagner   dirkwagner         997376 Apr 18 15:39 Schedule.sdc

Wenn die Shell-Option extglob aktiviert ist, werden erweiterte pattern matching Operatoren erkannt.

Extglob (erweitertes Globbing)

Die ksh88 führte zusätzlich die erweiterte Mustererkennung ein. Diese sind vergleichbar mit der Leistung Regulärer Ausdrücke, die Notation ist ähnlich, aber nicht gleich. Wenn die Shelloption extglob eingeschaltet ist, unterstützt auch die Bash diese Optionen.

Wenn möglich, sollte die erweiterte Mustererkennung regulären Ausdrücken vorgezogen werden, da diese effizienter und damit deutlich schneller ausgewertet werden können. Die Muster sind eine Liste von Zeichenketten, die durch | getrennt sind.

Das Zeichen vor der öffnenden Klammer reguliert die Auswertung des Musters:

?(Muster-Liste) Kein oder ein Auftreten eines Musters
*(Muster-Liste) Kein oder mehrere Auftreten eines Musters
+(Muster-Liste) Ein oder mehrere Auftreten eines Musters
@(Muster-Liste) Genau ein Auftreten eines Musters
!(Muster-Liste) Alle außer den angegebenen Mustern

Die erweiterte Mustererkennung kann mit Standard-Shell-Wildcards kombiniert und verschachtelt werden.

Übungen 1

  1. Vorbereitung
mkdir ~/gob && cd ~/gob
touch a aa aaa b bb bbb c cc ccc d dd ddd# Beliebig viele beliebige Zeichen (*)
ls a*
a  aa  aaa# Ein beliebiges Zeichen (?)
ls a?
aa# Ein Zeichen aus einer Menge [...]
ls [abc]
a  b  c# Ein Zeichen aus einem Bereich [ - ]
ls [a-d]
a  b  c  d# Kein Zeichen aus der Menge [! ]
ls [!ab]*
c  cc  ccc  d  dd  ddd# Kein Zeichen aus einem Bereich [! - ]
ls [!a-c]*
d  dd  ddd

Übungen 2

  1. Vorbereitung:
mkdir ~/extgob && cd ~/extgob
touch 1 11 111 2 22 222 3 33 333 
touch a aa aaa b bb bbb c cc ccc# ?(Muster-Liste)- kein oder ein Auftreten eines Musters
ls ?(a|b)
a  b# *(Muster-Liste) - kein oder mehrere Auftreten eines Musters
ls *(a|b)
a  aa  aaa  b  bb  bbb# +(Muster-Liste) - ein oder mehrere Auftreten eines Musters
ls +(a|b)
a  aa  aaa  b  bb  bbb# @(Muster-Liste) - genau ein Auftreten eines Musters
ls @(a|b)
a  b# !(Muster-Liste)- alle außer den angegebenen Mustern 
ls !(a|b)
1  11  111  2  22  222  3  33  333  aa  aaa  bb  bbb  c  cc  ccc# .!(|.)  - Dotfiles ohne "." und ".."
ls -d .!(|.)
.bash_history .bash_profile .bashrc# Zeichenklasse:
ls alpha:
a  b  c# Kombinationen
ls +(digit:)
1  11  111  2  22  222  3  33  333
ls ?(digit:)
1  2  3
ls +(digit:|!(upper:))
1  11  111  2  22  222  3  33  333  a  aa  aaa  b  bb  bbb  c  cc  ccc

Sicherheit

Dokumentation

RFC

Man-Pages

Info-Pages

Siehe auch

Links

Projekt-Homepage

Weblinks

Einzelnachweise


Testfragen

Testfrage 1

Antwort1

Testfrage 2

Antwort2

Testfrage 3

Antwort3

Testfrage 4

Antwort4

Testfrage 5

Antwort5

TMP

Tildeexpansion

siehe Bash:Tildeexpansion

Parameterexpansion

siehe Bash:Parameterexpansion

Kommandosubstitution

Die Kommandosubstitution erlaubt das Ersetzen ihres Aufrufes durch ihre Ausgabe. Es existieren zwei Syntaxvarianten des Aufrufs:

$(Kommando)
`Kommando`

Die Bash führt das Kommando aus und ersetzt seinen Aufruf auf der Kommandozeile durch dessen Ausgabe, wobei abschließende Zeilenendezeichen entfernt wurden.

# ohne Kommandosubstitution 
find / -name "whatis" 2>/dev/null | ls -l | head -5
insgesamt 15888
-rw-r--r--    1 user    users    787067 Apr  1 09:02 Buch.tar.gz
drwxr-xr-x    4 user    users      4096 Jan 16 19:49 Dhtml
drwx------    5 user    users      4096 Apr 26 09:48 Desktop
drwxr-xr-x    4 user    users      4096 Apr 21 08:43 IGLinux

# mit Kommandosubstitution
ls -l $(find / -name "whatis" 2>/dev/null)
ls -l $(find / -name "whatis"  2>/dev/null)
-rw-r--r--    1 root     root     94414 Jun 13 18:34 /usr/X11R6/man/whatis
-rw-r--r--    1 root     root    792270 Jun 13 18:34 /usr/man/allman/whatis
-rw-r--r--    1 root     root    220874 Jun 13 18:34 /usr/man/whatis
-rw-r--r--    1 root     root         0 Jun 13 18:34 /usr/openwin/man/whatis

Eine solche Kommandosubstitution kann auch bei der Zuweisung an eine Variable angewendet werden:

AktuellPfad=$(pwd)
echo $AktuellerPfad
/home/user

Zwischen Backquote (Accent Grave) gesetzte Kommandos werden ausgeführt und das Ergebnis wird dann als Parameter übergeben (d. h. die Ausgabe des Kommandos landet als Parameter in der Kommandozeile). Dabei werden Zeilenwechsel zu Leerzeichen. Braucht dieses Kommando Parameter, tritt die normale Parameterersetzung in Kraft. Zum Beispiel

echo "Aktuelles Verzeichnis: `pwd`"

Weil die verschiedenen Quotes manchmal schwer zu unterscheiden sind, wurde bei der bash eine weitere Möglichkeit eingeführt. Statt in Backquotes wird die Kommandofolge in $(...) eingeschlossen., z. B.:

echo "Aktuelles Verzeichnis: $(pwd)"

Command substitution allows the output of a command to replace the command itself. Command substitution occurs when a command is enclosed like this:

$(command) or like this using backticks: `command`

Bash performs the expansion by executing COMMAND and replacing the command substitution with the standard output of the command, with any trailing newlines deleted.

Embedded newlines are not deleted, but they may be removed during word splitting.

echo `date`
Thu Feb 6 10:06:20 CET 2003

When the old-style backquoted (accent aigu) form of substitution is used, backslash retains its literal meaning except when followed by "$", "`", or "\".

The first backticks not preceded by a backslash terminates the command substitution.

When using the "$(COMMAND)" form, all characters between the parentheses make up the command; none are treated specially.

Command substitutions may be nested. To nest when using the backquoted form, escape the inner backticks with backslashes.

If the substitution appears within double quotes, word splitting and file name expansion are not performed on the results.

Prozesssubstitution

Die Ein- bzw. Ausgabe von Prozessen kann mittels der Prozesssubstitution mit einer FIFO-Datei verbunden werden.

Taucht ein Konstrukt der Art <(Liste) bzw. >(Liste) auf, werden die durch Liste benannten Kommandos in einer Subshell gestartet. Gleichzeitig wird die Ausgabe (>(...)) bzw. Eingabe (<(...)) der Kommandos mit einer automatisch erzeugten FIFO-Datei verbunden.

Auf der Kommandozeile erscheint nach erfolgter Substitution der Name der erzeugten FIFO-Datei.

ls <(echo "hello")
/dev/fd/63

Mit Hilfe der Prozesssubstitution könnte man den vi dazu bewegen, die Ausgaben eines Kommandos zu lesen:

vi <(ls /boot/vm*)
/boot/vmlinuz
/boot/vmlinuz.old
~

~
"/dev/fd/63" [fifo/socket] 2L, 32C
                                       1,1            All

Ein weiteres Beispiel dient zur Bestandsaufnahme laufender Prozesse:

diff <(ps ax) <(sleep 10; ps ax)
64d63
<  2129 pts/0    S      0:00 /bin/bash
67,68c66
<  2132 pts/0    R      0:00 ps ax
<  2133 pts/0    S      0:00 sleep 10
---
>  2134 pts/1    S      0:00 top
>  2135 pts/0    R      0:00 ps ax

Im Beispiel ist der Prozess top neu hinzugekommen, dass die Aufrufe der Kommandos ps und sleep erscheinen, war zu erwarten.

Und abschließend vergleichen wir die Inhalte zweier Archive:

diff <(tar tzf Buch1.tar.gz) <(tar tzf Buch.tar.gz)
325a326,328
> Images/tkinfo.gif
> Images/GlobaleVariable.gif
> Images/LokaleVariable.gif

Innerhalb der Klammern >(...), <(...) können Parameter- Kommando- sowie arithmetische Substitutionen benutzt werden.

Arithmetische Substitution

Die Bash ist kein Taschenrechner. Dennoch besitzt sie ein erstaunliches Potenzial an eingebauten Rechenoperationen, die -- nach Prioritäten geordnet -- nachfolgende Tabelle zusammenfasst:

VAR++ and VAR-- variable post-increment and post-decrement
++VAR and --VAR variable pre-increment and pre-decrement
+ - Einstelliger Operator (Vorzeichen)
! ~ Logische und bitweise Negation
** Exponentialfunktion
* / % Multiplikation, Division und Modulo-Operator
+ - Addition, Subtraktion
<< >> Bitweise Links-/Rechtsverschiebung
<= >= < > Vergleiche
== != Gleichheit und Ungleichheit
& Bitweises UND
^ Bitweises Exclusive ODER
| Bitweises ODER
&& Logisches UND
Logisches ODER
expr ? expr : expr Bedingte Zuweisung
=, *=, /=, %=, +=, -= <<=, >>=, &=, ^=, |= Zuweisungen
, separator between expressions

Als Operanden sind Konstanten und Shellvariablen (deren Inhalt als long integer betrachtet wird) erlaubt. Beginnt eine Konstante mit "0", dann wird sie als oktale Zahl verstanden; steht "0x" am Anfang, handelt es sich um eine hexadezimale Konstante.

Konstanten können zu jeder Basis zwischen 2 und 64 angegeben werden, so kann die Zahl 63 u.a. wie folgt dargestellt werden: * Zur Basis 10: 10#63

  • Zur Basis 8: 8#77
  • Zur Basis 16: 16#3f

Die so genannte arithmetische Substitution ist der gebräuchliche Weg, um Berechnungen durchzuführen: * Bash Versionen <2: Der zu berechnende Ausdruck wird in eckigen Klammern geschrieben: $[...]

  • Bash ab Version 2: Der zu berechnende Ausdruck wird in doppelte runde Klammern geschrieben: $((...)) (die alte Syntax wird weiterhin unterstützt)

Arithmetic expansion allows the evaluation of an arithmetic expression and the substitution of the result. The format for arithmetic expansion is:

$(( EXPRESSION ))

The expression is treated as if it were within double quotes, but a double quote inside the parentheses is not treated specially.

All tokens in the expression undergo parameter expansion, command substitution, and quote removal. Arithmetic substitutions may be nested.

Evaluation of arithmetic expressions is done in fixed-width integers with no check for overflow - although division by zero is trapped and recognized as an error.

The operators are roughly the same as in the C programming language. In order of decreasing precedence, the list looks like this:

Shell variables are allowed as operands; parameter expansion is performed before the expression is evaluated.

Within an expression, shell variables may also be referenced by name without using the parameter expansion syntax.

The value of a variable is evaluated as an arithmetic expression when it is referenced. A shell variable need not have its integer attribute turned on to be used in an expression.

Constants with a leading 0 (zero) are interpreted as octal numbers. A leading "0x" or "0X" denotes hexadecimal. Otherwise, numbers take the form "[BASE'#']N", where "BASE" is a decimal number between 2 and 64 representing the arithmetic base, and N is a number in that base.

If "BASE'#'" is omitted, then base 10 is used. The digits greater than 9 are represented by the lowercase letters, the uppercase letters, "@", and "_", in that order.

If "BASE" is less than or equal to 36, lowercase and uppercase letters may be used interchangably to represent numbers between 10 and 35.

Operators are evaluated in order of precedence. Sub-expressions in parentheses are evaluated first and may override the precedence rules above.

Wherever possible, Bash users should try to use the syntax with square brackets:

$[ EXPRESSION ] 

However, this will only calculate the result of EXPRESSION, and do no tests:

echo $[365*24]
8760

Übungen

b=5; b=$((b+1)); echo $b

6

a=$((b+=10)); echo $a

16

echo $((a>b?1:0))

1

echo $((8#17**2))

225

echo $((017**2))

225

echo $((-0x64*3#11%6))

-4

echo $((4<<1))

8

Wird als Operand eine Variable benutzt, so wird versucht, deren Inhalt in eine Ganzzahl zu konvertieren. Enthält die Variable keine Zahl, wird der Inhalt zu "0" konvertiert:

b="Ist b keine Zahl, wird b zu 0 konvertiert"

echo $b
Ist b keine Zahl, wird b zu 0 konvertiert

b=$(($b+1)); echo $b

1

Funktionen

Eine Funktion ist ein Name für ein Kommando oder für eine Gruppe von Kommandos.

Funktionen werden vorrangig in Shellskripten verwendet, um wiederkehrende Kommandosequenzen nicht ständig neu schreiben zu müssen.

Ein großer Vorteil von Funktionen ist, dass sich diese in einer Datei speichern lassen und diese Datei von anderen Skripten geladen werden kann.

Eine Funktion wird wie folgt definiert:

[function] Funktionsname() { Kommando; [Kommando;] }

Bei der Verwendung von Funktionen sind einige Regeln zu befolgen: * Deckt sich der Name der Funktion mit einem builtin-Kommando, wird immer die Funktion ausgeführt und niemals das Kommando. Ebenso verdeckt ein Funktionsname ein gleichnamiges Kommando:

type test
test is a shell builtin
test(){ echo "ich bin eine Funktion"; }
type test
test is a function
test ()
{
echo "ich bin eine Funktion"
}
unset test* Die Funktion muss vor ihrer Verwendung definiert sein. 
  • Eine Funktion läuft in der aktuellen Umgebung, d.h. alle Variablen der Umgebung sind sichtbar und alle Variablen, die in der Funktion definiert wurden, sind auch außerhalb sichtbar:
func(){ var_in_func=xxx; }
func
echo $var_in_func
xxx* Wird eine Funktion mittels »exit« verlassen, wird auch der rufende Prozess beendet:
func(){ exit; }
func
login:* Der Rückgabewert einer Funktion ist der Rückgabewert des letzten in ihr gestarteten Kommandos: 
func(){ grep -q foo /etc/passwd; echo $?; }
func
1
echo $?
0
func(){ return 255; }
func
echo $?
255* Funktionen sind nur in der Shell ihrer Definition bekannt: 
func(){ echo "lokal"; }
bash
func
bash: func: command not found
exit
func
lokal

Einer Funktion können Parameter als Argumente übergeben werden:

Aufruf: Funktionsname [Arg1] [Arg2]...

Innerhalb einer Funktion kann auf die Parameter mittels der Positionsparameter $1..$9 zugegriffen werden. Als Beispiel dient eine Funktion "square", die die als Argument übergebene Zahl quadriert:

square() { test -z $1 && return 1; expr $1 \* $1; }
square
square 18
324

Erklärung

Das builtin-Kommando test liefert den Status "0", falls die Variable "$1" leer ist (kein Argument).

In diesem Fall wird "return 1" ausgeführt und der Funktionsaufruf beendet. expr berechnet den Ausdruck und schreibt das Ergebnis auf die Standardausgabe.

Wortzerlegung

In diesem Schritt wird die Kommandozeile in sogenannte Token unterteilt. Welche Zeichen als Separatoren verwendet werden, verrät die Variable $IFS (Internal Field Separator).

Ist diese nicht gesetzt, gelten die schon erwähnten Whitespaces als Begrenzer, sofern sie nicht innerhalb von (Doppel) Anführungsstrichen stehen oder durch den Backslash "maskiert" wurden.

The shell scans the results of parameter expansion, command substitution, and arithmetic expansion that did not occur within double quotes for word splitting.

The shell treats each character of $IFS as a delimiter, and splits the results of the other expansions into words on these characters.

If IFS is unset, or its value is exactly "'<space><tab><newline>'", the default, then any sequence of IFS characters serves to delimit words.

If IFS has a value other than the default, then sequences of the whitespace characters "space" and "Tab" are ignored at the beginning and end of the word, as long as the whitespace character is in the value of IFS (an IFS whitespace character).

Any character in IFS that is not IFS whitespace, along with any adjacent IF whitespace characters, delimits a field.

A sequence of IFS whitespace characters is also treated as a delimiter. If the value of IFS is null, no word splitting occurs.

Explicit null arguments ("""" or "") are retained. Unquoted implicit null arguments, resulting from the expansion of parameters that have no values, are removed. If a parameter with no value is expanded within double quotes, a null argument results and is retained.

[[Image:Grafik29.png]] Expansion and word splitting
If no expansion occurs, no splitting is performed.

Expansion von Dateinamen

After word splitting, unless the -f option has been set , Bash scans each word for the characters "*", "?", and "[".

If one of these characters appears, then the word is regarded as a PATTERN, and replaced with an alphabetically sorted list of file names matching the pattern.

If no matching file names are found, and the shell option nullglob is disabled, the word is left unchanged.

If the nullglob option is set, and no matches are found, the word is removed.

If the shell option nocaseglob is enabled, the match is performed without regard to the case of alphabetic characters.

When a pattern is used for file name generation, the character "." at the start of a file name or immediately following a slash must be matched explicitly, unless the shell option dotglob is set.

When matching a file name, the slash character must always be matched explicitly. In other cases, the "." character is not treated specially.

The GLOBIGNORE shell variable may be used to restrict the set of file names matching a pattern.

If GLOBIGNORE is set, each matching file name that also matches one of the patterns in GLOBIGNORE is removed from the list of matches.

The file names . and .. are always ignored, even when GLOBIGNORE is set.

However, setting GLOBIGNORE has the effect of enabling the dotglob shell option, so all other file names beginning with a "." will match.

To get the old behavior of ignoring file names beginning with a ".", make ".*" one of the patterns in GLOBIGNORE. The dotglob option is disabled when GLOBIGNORE is unset.

Metazeichen und Globbing

Damit man beim Angeben von z. B. Dateinamen nicht alle Namen eintippen muss, sondern die Dateien auch alle oder nach bestimmten Kriterien auswählen kann, gibt es Metazeichen (Jokerzeichen, Wildcards).

Metazeichen sind Zeichen mit erweiterter Bedeutung

Expansion von Dateinamen

Im Gegensatz zu anderen Systemen (z. B. Windows) werden Metazeichen von der Shell durch alle passenden Dateinamen ersetzt, bevor sie die Befehlszeile ausführt. Dies führt zu einem für Windows-Anwender ungewohntem Verhalten.

Metazeichen werden von der Shell ersetzt

Vorteile* liegt darin, dass, dass nahezu jedes UNIX-Kommando als Dateiangabe eine beliebige Menge von Dateien als Parameter haben kann.

  • So sind in Programmen keine Systemaufrufe nötig, die auf die Verzeichnisinformation zugreifen; es wird lediglich eine Schleife benötigt, welche die einzelnen Dateien nacheinander bearbeitet.

Praktisches Globbing

Ganz schlecht

chmod a+r witzig1.jpg
chmod a+r witzig2.jpg
chmod a+r witzig3.jpg

Besser

chmod a+r witzig1.jpg witzig2.jpg witzig3.jpg

Super

chmod a+r *.jpg

Es gibt folgende Metazeichen

* Der Stern steht für eine beliebige Zeichenfolge - oder für überhaupt kein Zeichen.

"ab*" steht für alle Dateinamen, die mit "ab" anfangen, auch für "ab" selbst ("ab", "abc", "abcd", "abxyz", usw.).

? Das Fragezeichen steht für genau ein beliebiges Zeichen. Zum Beispiel

"?bc" steht für alle Dateinamen mit 3 Zeichen, die auf "bc" enden ("abc", "bbc", "1bc", "vbc", "xbc", usw.), nicht jedoch für "bc".

[...] Im einfachsten Fall steht es für genau ein Zeichen aus der Menge (bspw. "[aeiou]" für einen Vokal).

Diese Angabe kann negiert werden ("alle außer diese Zeichen"), indem das erste Zeichen nach [ ein ! oder ^ ist ("[!abc]" bzw. "[^abc]").

Anstatt einzelne Zeichen aufzuzählen, lassen sich Bereiche angeben ("[a-z]" meint alle Kleinbuchstaben).

Wichtig!

Der * ist ein gefährliches Zeichen, Tippfehler könne zum Fiasko führen, wenn aus Versehen ein Leerzeichen zuviel getippt wird.

rm a* löscht beispielsweise alle Dateien, die mit "a" anfangen.
rm a * löscht dagegen erst die Datei "a" und dann alle Dateien im Verzeichnis. 

Zeichenbereiche

Außer grep und Regulären Ausdrücken, gibt es eine gute Auswahl an pattern matching, dass die Shell direkt durchführen kann, ohne dass dafür externe Programme eingesetzt werden müssen.

Bekannt sind sicherlich das Sternchen (asterisk) „*“ und das Fragezeichen (question mark) „?“, die jede Zeichenkette oder jeden Buchstaben filtern. Um diese Sonderzeichen als Literale zu filtern müssen sie maskiert werden:

touch "*"
ls "*"
*

But you can also use the square braces to match any enclosed character or range of characters, if pairs of characters are separated by a hyphen. An example:

ls -ld [a-cx-z]*
drwxr-xr-x    2 dirkwagner    dirkwagner          4096 Jul 20  2002 app-defaults/
drwxrwxr-x    4 dirkwagner    dirkwagner          4096 May 25  2002 arabic/
drwxrwxr-x    2 dirkwagner    dirkwagner          4096 Mar  4 18:30 bin/
drwxr-xr-x    7 dirkwagner    dirkwagner          4096 Sep  2  2001 crossover/
drwxrwxr-x    3 dirkwagner    dirkwagner          4096 Mar 22  2002 xml/

This lists all files in dirkwagner's home directory, starting with "a", "b", "c", "x", "y" or "z".

If the first character within the braces is "!" or "^", any character not enclosed will be matched.

To match the dash ("-"), include it as the first or last character in the set.

The sorting depends on the current locale and of the value of the LC_COLLATE variable, if it is set.

Mind that other locales might interpret "[a-cx-z]" as "[aBbCcXxYyZz]" if sorting is done in dictionary order.

If you want to be sure to have the traditional interpretation of ranges, force this behavior by setting LC_COLLATE or LC_ALL to "C".

Nullglob

Ohne Match wird das Argument unverändert übernommen. Dieses Verhalten lässt sich mit der Shelloption nullglob beeinflussen.

Match - gefundene Dateien werden übernommen

echo a1*.jpg
a1.jpg a10.jpg a11.jpg

Kein Match - Argument bleibt unverändert

echo b1*.jpg
b1*.jpg

Dotfiles

Der Punkt am Anfang von Dateinamen stellt eine Ausnahme dar, er muss explizit angegeben werden (wegen der Verzeichnisreferenzen "." bzw. ".." und der Tatsache, dass Dateien, die mit einem Punkt beginnen, normalerweise nicht angezeigt werden). * Dateien, die mit "." beginnen sind versteckt

  • Shell Globbing ignoriert die "Dotfiles"
  • Punkt muss explizit genannt werden
echo .*
. .. .bash_history .bash_profile .bashrc

Globbing und Brace Expansion

Brace Expansion hat starke Ähnlichkeit mit Globbing doch die angegebenen Dateinamen müssen nicht existieren.

ls 
a1.jpg a10.jpg a11.jpg a2.jpg a3.jpg a4.jpg

"{name1,name2}" - Alternativen aufzählen

echo a{2,10}.jpg
a2.jpg a10.jpg

Zeichenklassen

Zeichenklassen können mit folgender Syntax in eckigen Klammen angegeben [:CLASS:]. Dabei wird CLASS nach POSIX-Standard angegeben und kann folgende Werte annehmen:

"alnum", "alpha", "ascii", "blank", "cntrl", "digit", "graph", "lower", "print", "punct", "space", "upper", "word" or "xdigit"

ls -ld digit:*
drwxrwxr-x    2 dirkwagner   dirkwagner           4096 Apr 20 13:45 2/
ls -ld upper:*
drwxrwxr--    3 dirkwagner   dirkwagner           4096 Sep 30  2001 Nautilus/
drwxrwxr-x    4 dirkwagner   dirkwagner           4096 Jul 11  2002 OpenOffice.org1.0/
-rw-rw-r--    1 dirkwagner   dirkwagner         997376 Apr 18 15:39 Schedule.sdc

Wenn die Shell-Option extglob aktiviert ist, werden erweiterte pattern matching Operatoren erkannt.

Extglob (erweitertes Globbing)

Die ksh88 führte zusätzlich die erweiterte Mustererkennung ein. Diese sind vergleichbar mit der Leistung Regulärer Ausdrücke, die Notation ist ähnlich, aber nicht gleich. Wenn die Shelloption extglob eingeschaltet ist, unterstützt auch die Bash diese Optionen.

Wenn möglich, sollte die erweiterte Mustererkennung regulären Ausdrücken vorgezogen werden, da diese effizienter und damit deutlich schneller ausgewertet werden können. Die Muster sind eine Liste von Zeichenketten, die durch | getrennt sind.

Das Zeichen vor der öffnenden Klammer reguliert die Auswertung des Musters:

?(Muster-Liste) Kein oder ein Auftreten eines Musters
*(Muster-Liste) Kein oder mehrere Auftreten eines Musters
+(Muster-Liste) Ein oder mehrere Auftreten eines Musters
@(Muster-Liste) Genau ein Auftreten eines Musters
!(Muster-Liste) Alle außer den angegebenen Mustern

Die erweiterte Mustererkennung kann mit Standard-Shell-Wildcards kombiniert und verschachtelt werden.

Übungen 1

  1. Vorbereitung
mkdir ~/gob && cd ~/gob
touch a aa aaa b bb bbb c cc ccc d dd ddd# Beliebig viele beliebige Zeichen (*)
ls a*
a  aa  aaa# Ein beliebiges Zeichen (?)
ls a?
aa# Ein Zeichen aus einer Menge [...]
ls [abc]
a  b  c# Ein Zeichen aus einem Bereich [ - ]
ls [a-d]
a  b  c  d# Kein Zeichen aus der Menge [! ]
ls [!ab]*
c  cc  ccc  d  dd  ddd# Kein Zeichen aus einem Bereich [! - ]
ls [!a-c]*
d  dd  ddd

Übungen 2

  1. Vorbereitung:
mkdir ~/extgob && cd ~/extgob
touch 1 11 111 2 22 222 3 33 333 
touch a aa aaa b bb bbb c cc ccc# ?(Muster-Liste)- kein oder ein Auftreten eines Musters
ls ?(a|b)
a  b# *(Muster-Liste) - kein oder mehrere Auftreten eines Musters
ls *(a|b)
a  aa  aaa  b  bb  bbb# +(Muster-Liste) - ein oder mehrere Auftreten eines Musters
ls +(a|b)
a  aa  aaa  b  bb  bbb# @(Muster-Liste) - genau ein Auftreten eines Musters
ls @(a|b)
a  b# !(Muster-Liste)- alle außer den angegebenen Mustern 
ls !(a|b)
1  11  111  2  22  222  3  33  333  aa  aaa  bb  bbb  c  cc  ccc# .!(|.)  - Dotfiles ohne "." und ".."
ls -d .!(|.)
.bash_history .bash_profile .bashrc# Zeichenklasse:
ls alpha:
a  b  c# Kombinationen
ls +(digit:)
1  11  111  2  22  222  3  33  333
ls ?(digit:)
1  2  3
ls +(digit:|!(upper:))
1  11  111  2  22  222  3  33  333  a  aa  aaa  b  bb  bbb  c  cc  ccc

Unterkategorien

Diese Kategorie enthält nur die folgende Unterkategorie:

B