Nützliches für die Kommandozeile
Übersicht über das Konsolen-Tutorial
Zerlegung in Eingabeelemente
Eine Shell zerlegt die Eingabe in einzelne Eingabeelemente, die dann mit den unten beschriebenen Ersetzungen bearbeitet werden.
- Ein String ist eine mit Stringtrennern eingeschlossene Zeichenfolge.
- Stringtrenner sind einfacher und doppelter Apostroph: ' und ".
- Ein Wort ist die jeweils längstmögliche Zeichenfolge ohne Trennzeichen
- Trennzeichen sind in der Variablen $IFS festgelegt, standardmäßig Leerzeichen, TAB und NEWLINE.
- Ist ein Eingabelement ein String, ist es kein Wort
echo "1 oder 2" Beispiele 'gefällig?'
Die Shell erkennt 4 Eingabeelemente:
- echo
- "1 oder 2"
- Beispiele
- 'gefällig?'
Ersetzungen
Die Bash kennt eine ganze Reihe von Abkürzungen und Ersetzungen:
Ergänzung des Dateinamens
Kommen in einem Wort (nicht String) die Zeichen *, [] oder ? vor, wird dieses Wort durch alle Dateinamen ersetzt, die auf das dadurch gegebene Muster passen. Muster deshalb, weil die oben genannten Platzhalter auf mehrere Dateinamen passen können.
Die Bedeutung der Platzhalter:
- *: Es können beliebig viele Zeichen an dieser Stelle sein, also auch gar kein Zeichen.
- ?: Es ist genau ein Zeichen vorhanden.
- [<liste>]: Es kommt genau ein Zeichen aus der Liste vor. Die Liste besteht aus einer Aufzählung von Einzelzeichen und von Bereichen, wobei letztere durch Anfang, '-' und Ende gebildet werden.
- [aeiou]: Es kommt genau ein Vokal vor
- [0-9a-fA-F]: Es kommt genau eine Hexziffer vor
- [^<liste>]: Es kommt kein Zeichen aus der Liste vor.
- [^0-9]: Ein Zeichen, das keine Ziffer ist
- [^-._]: Ein Zeichen, aber nicht '-', '.' oder '_'
Beispiele
Voraussetzung für Nachmachen schaffen:
mkdir -p /tmp/bashtest/dir ; cd /tmp/bashtest ; touch a1.txt a2.txt alles.txt nix.txt a10.txt dir/b1.txt dir/b2.txt
Fängt mit a an, hört mit .txt auf:
echo a*.txt
a10.txt a1.txt a2.txt alles.txt
Fängt mit zwei Zeichen an, hört mit .txt auf:
echo ??.txt
a1.txt a2.txt
Da Verzeichnisse unter Unixoiden genauso Dateien sind, funktioniert auch:
Verzeichnis fängt mit d an, im Dateinamen kommt eine '1' oder '2' vor:
echo d*/*[12]*
dir/b1.txt dir/b2.txt
Aufgabe: Gib einen Ausdruck an, mit dem diese Ausgabe erzeugt wird:
alles.txt nix.txt
Negation
Manchmal ist es nützlich, Ausnahmen angeben zu können. Das geht mit !(<muster>):
echo !(??.*)
Alles, was nicht 2 Zeichen vor .txt hat:
a10.txt alles.txt dir nix.txt
Jedes Wort einzeln
Die Shell expandiert jedes Wort einzeln. Es werden also keine Dupletten ausgesiebt.
echo !(*.txt) a* ??.txt
dir a10.txt a1.txt a2.txt alles.txt a1.txt a2.txt
- Das erste Wort erzeugt dir
- Das zweite Wort erzeugt a10.txt a1.txt a2.txt alles.txt
- Das 3.te Wort erzeugt a1.txt a2.txt
Generelle Ausnahmen
Mit der Variablen $GLOBIGNORE kann Muster angeben, getrennt mit ':'. Jede Datei, die auf eines dieser Muster passt, erscheint nicht in der Expansion.
GLOBIGNORE=*1.*:?i*
echo *
a10.txt a2.txt alles.txt
Die geschweiften Klammern
Mit geschweiften Klammern können Wörter durch Kombination erzeugt werden. Dies ist eine rein textuelle Operation und hat nichts mit Dateinamen zu tun!
Die erste Form nutzt Aufzählungen:
echo Fall{1,2,5}
Fall1 Fall2 Fall5
Die zweite Form operiert auf Intervallen, evt. mit Schrittweite:
echo Fall_{1..4}
echo Plan_{10..16..2}
# auch mehrfach (dann alle Kombinatinen):
echo X{a..f}{0..2}
Fall_1 Fall_2 Fall_3 Fall_4 Plan_10 Plan_12 Plan_14 Plan_16 Xa0 Xa1 Xa2 Xb0 Xb1 Xb2 Xc0 Xc1 Xc2 Xd0 Xd1 Xd2 Xe0 Xe1 Xe2 Xf0 Xf1 Xf2
Tilde
- Steht das Zeichen ~ am Anfang eines Wortes (nicht Strings), wird diese durch die Variable $HOME (das Heimatverzeichnis des angemeldeten Benutzers) ersetzt.
- Steht am Anfang ein ~+, findet eine Ersetzung mit $PWD (aktuelles Verzeichnis) statt.
- Beginnt das Wort mit ~-, wird $OLDPW eingesetzt (Vorgänger des aktuellen Verzeichnisses)
cd /tmp ; cd /tmp/bashtest
echo ~ XXX ~+/dir YYY ~-/bash*
/home/hm XXX /tmp/bashtest/dir YYY /tmp/bashtest
Die Tilde-Ersetzung wird zuerst durchgeführt, dann erfolgt noch eine Ergänzung der Dateienamen.
Anwendungsbeispiele
Alle Textdateien des aktuellen Verzeichnisses in den Ordner merken im Heimatverzeichnis legen:
mkdir ~/merken ; ln -s ~+/*.txt ~/merken
Hinweis: Das Kommando ln benötigt in diesem Fall absolute Pfade.
Ein vorausgegangenes cd (change directory) rückgängig machen:
cd ~-
Expansion verhindern (Quoting)
Was machen wir, wenn wir keine Dateinamen- oder Geschweiftklammer-Expansion wollen? Dazu gibt es 2 Methoden:
- Vor die Sonderzeichen einen Backslash \ setzen: echo geht es noch\?
- Aus dem Wort einen String machen:
- echo "Geht es noch?"
- echo 'Geht es noch?'
Anwendungsbeispiel
Das prominenteste Beispiel ist wohl die Verwendung von -exec im Kommando find. Mit dieser Option kann man mit allen gefundenen Dateien "was machen". An der Stelle, an der die Datei in der Ausgabe erscheinen soll, ist der Platzhalter {} einzusetzen, das Ende der Ausgabe ist mit ; zu markieren.
Wir wollen die Anzahl der Wörter in allen Textdateien im Unterverzeichnis dir zählen:
find dir -name *.txt -exec wc -w {};
find: Der Pfad muß vor dem Suchkriterium stehen: a2.txt
Was ist passiert? Schauen wir der Shell zu, was sie macht:
set -x
find dir -name *.txt -exec wc -w {} ;
+ find dir -name a10.txt a2.txt alles.txt -exec wc -w '{}' find: Der Pfad muß vor dem Suchkriterium stehen: a2.txt
Die Bash hat den Ausdruck *.txt expandiert (in a10.txt a2.txt alles.txt), damit stimmt die Syntax von find nicht mehr. Also müssen wir die Expansion verhindern durch Stringbildung "*.txt".
Aber wo ist der Strichpunkt am Ende geblieben? Die Bash hat den Strichpunkt als Trenner zum nächsten Kommando aufgefasst und entfernt. Daher müssen wir quoten, z.B. mit \;
find dir -name "*.txt" -exec wc -w {} \;
set +x
+ find dir -name '*.txt' -exec wc -w '{}' ';' 0 dir/b1.txt 0 dir/b2.txt
Jetzt leistet find das Gewünschte.
Hinweis: Oft findet man in Beispielen von find, dass auch {} gequotet wird. In der Bash ist das, wie oben gesehen, nicht notwendig: Die geschweiften Klammern werden nur ersetzt, wenn sich darin eine Liste befindet.
Strings und Variablenersetzung
Der Unterschied von und "": Variablen werden innerhalb "" ersetzt, in nicht:
echo "Ich bin in $HOME zu Hause"
Ich bin in /home/hm zu Hause
echo '$HOME ist das Heimatverzeichnis'
$HOME ist das Heimatverzeichnis