Suchen, Reguläre Ausdrücke, Variablen etc.¶
Einstieg in die Bash-Shell - Teil 2
ungekürzte Fassung
Lernziele:¶
- Erlernen, wie Dateien nach verschiedenen Kriterien gefunden werden können.
- Kennenlernen von regulären Ausdrücken.
- Kennenlernen von Variablen
- Kennenlernen von Substitutionen
- Kennenlernen von Subshells
Cheatsheets¶
Hinweis: Da die Benutzung der Bash teilweise kryptisch und die Syntax schwer zu merken ist, können Sie Cheatsheets für die Übungen etc. verwenden:
Hier finden Sie eine Liste der GNU Core Utils. Das sind Standardkommandos, die in den meisten Unix-Umgebungen verfügbar sind.
Vorbereitung¶
Hinweis:
- Eventuell müssen Sie vorab das Versionskontrollsystem
git
installieren, z.B. bei Debian-basierten Linux-Systemen (wie Ubuntu) mittelssudo apt-get install git-all
.
Klonen Sie (in einem geeigneten Verzeichnis) das Python-Poetry Code-Repositoy mittels:
git clone https://github.com/python-poetry/poetry
Suchen¶
find
¶
Mit Hilfe von find
können Sie ausgehend von einem Verzeichnis Dateien finden. Dabei können verschiedene
Filter gesetzt werden, die die gefundenen Dateien erfüllen müssen:
-type
: Typ der Datei, z.B. Verzeichnis (d
), normale Datei (regular file)f
etc.-name
: Name der Datei, hier können glob-Muster, wie*
,?
, ranges ([..]
mit Negation[^..]
verwendet werden.-iname
: wie-name
, aber case-insensitiv (d.h. insensitiv auf Groß- und Kleinschreibung)-path
(case-insensitive-ipath
): analog-name
, aber das glob-Muster bezieht sich auf den vollständige Pfad zur Datei. So kann man Pfade einschränken und den Verzeichnistrenner/
verwenden. Vergleich Manpage vonfind
für den Unterschied zwischenname
undpath
.-mtime
Modifikationsdatum (-atime
Zugriffsdatum) in 24h Einheiten-mmin
Modifikationsdatum in Minuten-size
Größe
Beispiel: Alle Verzeichnisse ausgehend vom Home-Verzeichnis, die im Namen "HTW" enthalten: find ~ -name "*HTW*" -type d
Für weitere Optionen, wie Suche nach Datum, Benutzer etc. siehe https://wiki.ubuntuusers.de/find/
Für eine ausführliche Dokumentation, siehe https://www.gnu.org/software/findutils/manual/html_mono/find.html#Full-Name-Patterns. Aber natürlich gibt es auch eine man-Page (man find
) und https://tldr.ostera.io/find.
Etliche Beispiele finden Sie auch hier: https://alvinalexander.com/unix/edu/examples/find.shtml
Wiederholung: Beachten Sie, dass die Shell Wildcards -falls möglich- per Globbing direkt auflöst, bevor die Argumente zum
Befehl (hier find
) weitergereicht werden. Mit find ~/development -name *.py
würde das *.py
durch die im laufenden Arbeitsverzeichnis befindlichen Python-Dateien ersetzt werden, wenn hier Dateien mit Endung .py
vorhanden sind.
Daher ist unbedingt ein Quoting vozunehmen:find ~/development -iname "*.py"
. Quoting schützt das Glob-Muster davor, dass die Shell es gegebenenfalls expandiert (mehr dazu siehe unten).
Hinweise zu den Mengenangaben, wie z.B. Zeitangaben:¶
-
und +
vor der Mengenangabe bedeutet kleiner bzw. größer, z.B.:
Finde alle Dateien unter dem Download Ordner, die vor weniger als 24h (1 Tag) modifiziert (oder erstellt) worden sind:
find ~/Downloads/ -mtime -1
Beachte: Finde alle Dateien unter dem Download Ordner, die genau 24h alt sind. Das will man in der Regel nicht!
find ~/Downloads/ -mtime 1
Finde alle Dateien unter dem Download Ordner, die älter als 24h sind:
find ~/Downloads/ -mtime +1
Neben mtime
gib es auch
atime
für accessed. Es wurde auf die Datei zugegriffen.ctime
für changed, d.h. der Dateistatus hat sich geändert.
Weitere Beispiele¶
Probieren Sie dies selbst aus und verändern Sie auch die Befehle, um mehr zu lernen. Wechseln Sie dazu in das Verzeichnis, in dem Sie das poetry
-Projekt gespeichert haben.
Finde ausgehend vom laufenden Arbeitsverzeichnis alle Verzeichnisse (vom Type directory) mit Namen "poetry":
find . -name "poetry" -type d
Finde ausgehend vom Arbeitsverzeichnis alle Dateien mit Namen beginnend mit read
(beliebige Groß-Kleinschreibung - case insensitiv):
find . -iname "read*"
Finde alle regulären Dateien mit Endung ".py" unter dem laufenden Arbeitsverzeichnis, die im Pfad ein Verzeichnis "poetry" haben:
find ~ -path "*/poetry/*.py" -type f
Finde alle python-Dateien (Endung "*-py"), die zwischen 10k und 15k groß sind:
find . -size +10k -size -15k -name '*.py'
Beachten Sie, dass in der Regel das Glob-Muster in Anführungszeichen gesetzt werden muss (Quoting),
damit die Shell es nicht vor dem Weiterleiten zu find
auflöst.
Aufgabe¶
Finden Sie alle Verzeichnisse mit "test" im Verzeichnisnamen.
So soll z.B. ./repositories/fixtures/pypi.org/json/pytest/
gefunden werden.
Aufgabe¶
Welche von den folgenden Dateien werden bei
1.find . -name "*[0-9]*.ipynb"
2.find . -iname "[[:digit:]]*.ipynb"
gefunden?
30-Beispiel.ipynb
a-30-Beispiel.ipynb.txt
a-30.ipynb
Beispiel.ipynb
2-a.IPYNB
Annahme: Alle Dateien liegen unter dem laufenden Arbeitsverzeichnis (.
) entweder direkt oder in Unterverzeichnissen.
Probieren Sie es auch aus! Legen Sie hierzu ein Unterverzeichnis (zum Ausprobieren) in Ihrem Heimatverzeichnis an. Erzeugen Sie in diesem Verzeichnis die Dateien
30-Beispiel.ipynb a-30-Beispiel.ipynb.txt a-30.ipynb Beispiel.ipynb 2-a.IPYNB
an.
Erinnerung: Befehl touch
Aufgabe¶
Finden Sie alle C++-Dateien (Annahme: erkennbar an der Endung .cpp
), die in den letzten 5 Tagen (genauer 5 $\cdot$ 24h) modifiziert wurden (ausgehend vom Arbeitsverzeichnis).
find "${poetry_path}" -name "*.lock" | xargs rm
# oder ausführlicher
#find "${poetry_path}" -name "*.lock" | xargs -I {} rm {}
locate
¶
Hinweis: Neben find
gibt es auch den Befehl locate
zum Finden von Dateien. Dieser benutzt eine Datenbank, die ab und an aktualisiert wird. Daher ist locate
schneller. Es kann aber passieren, dass neue Dateien bzw. geänderte Dateinamen nicht gefunden werden. Die Datenbank wird mit Hilfe eines cron-Jobs (mehr zu cron
später) in der Regel täglich aktualisiert. Sie kann auch mittels sudo updatedb
manuell aktualisiert werden.
Mehr zu locate
in der Manpage oder unter https://wiki.ubuntuusers.de/locate/
Reguläre Ausdrücke¶
Mit Hilfe Regulärer Ausdrücke (regular expressions, kurz Regex) können Zeichenketten (Texte) auf Muster durchsucht werden oder überprüft werden etc. Bei einer Suche kann man beispielsweise als Ergebnis die Bereiche (typischerweise Zeilen) erhalten, in denen das Muster gefunden wird.
Reguläre Ausdrücke gibt es für alle gängigen Programmiersprachen und auch für viele Textanwendungen (Editoren oder Textverarbeitungsprogramme) zur Suche. Dabei gibt es oft kleine Unterschiede bzgl. der Syntax der Ausdrücke (verschiedene Regex-Dialekte). Ein weiteres Anwendungsbeispiel neben der Suche ist die Überprüfung von Usereingaben, z.B. ob in einem Webformularen eine gültige E-Mail Adresse eingegeben wurde.
Reguläre Ausdrücke vs. Dateinamen-Globbing¶
Beachten Sie, dass reguläre Ausdrücke und Dateinamen-Globbing zwei unterschiedliche Dinge sind.
Hinweis: Zu Glob gibt es auch eine Manpage: man glob
grep
¶
Der Befehl grep
dient zum Finden von Text-Mustern ausgedrückt durch reguläre Ausdrücke, z.B. in stdout
oder Textdateien.
Arbeiten Sie folgende Beschreibung von RegEx (eventuell parallel mit der Regex-Übung, siehe unten) und grep durch: https://wiki.ubuntuusers.de/grep/
Hinweis: Es gibt auch eine Manpage: man regex
Hinweis: Es gibt verschiedene Dialekte bei Regulären Ausdrücken.
Wir verwenden Posix Regular Expressions: Schalter -E
bei grep
. Alternativ können Sie den Befehl
egrep
nutzen, der grep -E
entspricht.
Übung¶
Arbeiten Sie die Übung "Reguläre Ausdrücke" (jupyter notebook regular-expression-exercise.ipynb
) durch.
Beachten Sie, dass die gängige Regex-Syntax den extended regular expressions (posix) der Bash entspricht.
Tipp zum Testen von regulären Ausdrücken auf der Kommandozeile können Sie auch ein here-Dokument verwenden (here-Dokumente werden weiter unten genauer erklärt):
grep -E "[^a][Bb]er" << EOF
Aber heute war es toll
in Berlin.
Hier können noch
mehr Zeilen stehen,
müssen aber nicht.
EOF
Aber heute war es toll in Berlin.
Übung¶
Aufgabe¶
- In allen python-Dateien (Endung
.py
) des poetry-Projektes, dierepository
im Namen haben, soll das TextmusterTODO
gefunden werden. - In allen python-Dateien (Endung
.py
) des poetry-Projektes soll das Textmusterplatform
gefolgt von beliebig vielen Zeichen und dann die Zeichenfolgewheels
gefunden werden, z.B. soll folgende Zeile ausgegeben werden:if platform_specific_wheels:
Zwei mögliche Lösungswege:
- Nutzen des
-exec
Schalters vonfind
fürgrep
. - Pipen des stdout-outputs des
find
-Kommandos und verwenden vonxargs
undgrep
.
Aufgabe¶
Sie wollen wissen, aus wievielen python-Dateien das Softwareprojekt poetry besteht, d.h. Sie wollen Anzahl aller python-Dateien (Endung .py
) unter dem Projekt-Hauptverzeichnis zählen.
Aufgabe¶
Sie wollen wissen, aus wievielen Zeilen python-Code das Softwareprojekt poetry besteht, d.h. Sie wollen die Zeilen aller python-Dateien (Endung .py
) unter dem Projekt-Hauptverzeichnis zählen.
Wie können Sie dies machen?
Hinweis: Kombinieren Sie die Befehle find
, cat
und wc
.
cat
mit mehreren Dateien als Argumente gibt den Inhalt dieser Dateien gemeinsam (auf stdout) aus (concatenation).wc
hat einen Schalter zum Zählen der Zeilen.
Aufgabe¶
Mit Hilfe des Kommandos history
kann man die zuletzt verwendeten Befehle erhalten.
Welche zuletzt verwendeten Befehle haben Sie benutzt, die find
und xargs
enthalten?
Ihre Befehlskette soll nur diese Befehle ausgeben.
Randmerkung: Diese Information wird in der Datei ~/.bash_history
abgespeichert. Nutzen Sie aber nicht diese Datei direkt, sondern verwenden Sie den Befehl history
.
Aufgabe¶
Wie kann man nur die Dateien im Arbeitsverzeichnis mit Endung .ipynb
anzeigen (unter der Benutzung von ls
), deren Dateinamen mindestens 2 Zahlen in Folge enthalten?
Hinweis: Erzeugen Sie ggf. zum ausprobieren die Dateien, in einem geeigneten Verzeichnis.
find
direkt mit regulären Ausdrücken¶
Mit Hilfe des Schalters -regex
kann bei find
anstatt eines Glob-Musters auch ein
regulärer Ausdruck verwendet werden. Welcher Art (Dialekt) von regulären Ausdrücke angewendet werden soll, kann mittels -regextype
eingestellt werden.
Beispiel:
find .. -maxdepth 1 -regextype "posix-extended" -regex '.*[0-9]{1,10}.*\.ipynb'
echo
echo ------------
echo
# ist äquivalent zu
ls ../*.ipynb | grep -E '[0-9]{1,10}'
../bash_3.ipynb ../bash_5.ipynb ../bash_4.ipynb ../bash_1.ipynb ../bash_2.ipynb ------------ ../bash_1.ipynb ../bash_2.ipynb ../bash_3.ipynb ../bash_4.ipynb ../bash_5.ipynb
Variablen¶
In Bash können Werte in Variablen gespeichert werden.
Um einen Wert einer Variablen zuzuordnen, wird der Zuweisungsoperator =
verwendet.
my_var=5
Beachten Sie dabei, dass hier keine Leerzeichen beim =
stehen dürfen.
Warum ist das so restriktiv? Probieren Sie es aus mit einem Leerzeichen und erklären Sie die Fehlermeldungen.
Beachten Sie das dies zu Beginn verwirrend ist und leicht zu Fehlern führen kann.
Nebenbemerkung: In der Shell-Terminologie ist eine Variable auch ein Shell Parameter.
Ein Parameter ist eine Entität, die einen Wert enthalten kann. Eine Variable ist ein Parameter mit einem Namen (hier my_var
).
Der Inhalt einer Variablen kann mit Parameter Expansion
ausgelesen werden. Parameter Expansion startet mit einem $
, z.B.:
echo $my_var
5
Was gibt dagegen echo my_var
aus? Probieren Sie es aus.
Manchmal muss man geschweifte Klammern setzen, um den Variablennamen vom Folgenden zu trennen, wie hier
echo ${my_var}er
5er
Was passiert, wenn man hier die gescheiften Klammern weglässt und warum ist dies so?
Randbemerkung: Der grundlegende Type einer bash-Variable ist String. Mit declare
können aber Attribute gesetzt werden, wie z.B. das integer attribute.
Da im Prinzip alle Variablen Strings sind, hat die bash(-Programmiersprache) nicht wirklich Typen, vgl. Unix-Philosophie: "Schreibe Programme so, dass sie Textströme verarbeiten, denn das ist eine universelle Schnittstelle."
In diesem Sinne ist bash sehr einfach und dient oft nur einfachen administrativen Aufgaben (nahe am Betriebsystem bzw. Befehlsinterpreter).
Für komplexere adminstrative Aufgaben wird daher oft die Programmiersprache Python eingesetzt, die erheblich mächtiger ist und verschiedene Datentypen kennt.
Quoting¶
Längere Strings mit Leerzeichen können durch die Anführungszeichen "
oder '
geschützt werden (Quoting).
greetings="Hallo Welt!"
Beachte den Unterschied zwischen "
und '
:
my_var="-$greetings- ist ein Gruß." # Variablen Expansion
echo $my_var
-Hallo Welt!- ist ein Gruß.
my_var='-$greetings- ist ein Gruß.' # alle Zeichen (auch $) werden als Literale interpretiert
echo $my_var
-$greetings- ist ein Gruß.
Bei den einfachen Anführungszeichen werden die Inhalte direkt angezeigt, ohne dass z.B. Variablen expandiert werden. Erinnerung: bei doppelten Anführungszeichen wird das Globbing von der Shell nicht aufgelöst:
#`-e` enable interpretation of backslash escapes for newline via \n
echo -e "Quoting: *.txt \nvs. \nExpansion:" *.txt
Quoting: *.txt vs. Expansion: countries_1.txt countries_2.txt countries_3.txt countries_4.txt countries_second_hard_link.txt countries.txt f2.txt f.txt inhalt_des_verzeichnisses.txt ls-out.txt mylog.txt rechnernamen.txt stderr.txt text_with_urls.txt t.txt
Beachte: Einige Zeichen, wie $
oder &
, müssen escaped werden:
echo Du schuldest mir $5! # Erklären Sie die Ausgaben!
echo "Du schuldest mir $5!"
echo "Du schuldest mir \$5!"
echo 'Du schuldest mir $5!' # escaping nicht nötig bei "single quotes"
Du schuldest mir ! Du schuldest mir ! Du schuldest mir $5! Du schuldest mir $5!
Substrings¶
Mit geschweiften Klammern und Doppelpunkten kann man Teile der Zeichfolge einer Variablen erhalten:
echo ${greetings}
echo ${greetings:0:5}
Length=5
echo ${greetings:0:Length}
Hallo Welt! Hallo Hallo
echo ${greetings: -5} # Das Leerzeichen ist wichtig!
Welt!
# sonst geht es nicht:
echo ${greetings:-5}
Hallo Welt!
String Länge¶
Mit geschweiften Klammern und Doppelpunkten kann man Länge des Variableninhaltes (hier die Länge der Zeichenfolge).
echo "\"$greetings\" hat ${#greetings} Zeichen."
"Hallo Welt!" hat 11 Zeichen.
Ersetzungen und Beseitigungen in Variablen¶
Folgende Syntax dient zur Ersetzung von Teilen des Variableninhaltes bei der Variablenexpansion:
Beseitigungen mit Mustern am Anfang und Ende¶
Beseitigung am Anfang:
${var#Muster}
: Beseitigt kürzesten Teil des Musters${var##Muster}
: Beseitigt längsten Teil des Musters
Beseitigung am Ende:
${var%Muster}
: Beseitigt kürzesten Teil des Musters${var%%Muster}
: Beseitigt längsten Teil des Musters
Ersetzungen am Anfang:
${var/#from/to}
: Ersetzt das Muster am Anfang
Ersetzungen allgemein:
${var/from/to}
: Ersetze ersten Treffer${var//from/to}
: Ersetze alle
Beispiele:
echo $greetings
# Ersetze "Hallo" mit "Grüße an die" in der Variable greetings
echo ${greetings/Hallo/Grüße an die}
Hallo Welt! Grüße an die Welt!
# Machen Sie sich das Ergebnis klar!
var="http://christianherta.de/lehre/informatik/von_neumann_architektur.html"
echo "${var#*/}"
echo "${var##*/}"
/christianherta.de/lehre/informatik/von_neumann_architektur.html von_neumann_architektur.html
echo ${var/#*\//path-to-file:}
#echo ${var/##*\//path-to-file:}
path-to-file:von_neumann_architektur.html
echo ${var/e/a}
echo ${var//e/a}
http://christianharta.de/lehre/informatik/von_neumann_architektur.html http://christianharta.da/lahra/informatik/von_naumann_architaktur.html
var=/path/to/a/image.old.png
echo "${var%png}"jpg
echo "${var%.*}".jpg
echo "${var%%.*}".jpg
/path/to/a/image.old.jpg /path/to/a/image.old.jpg /path/to/a/image.jpg
Mehr zu String-Substitutionen siehe: https://www.cyberciti.biz/tips/bash-shell-parameter-substitution-2.html
Indirekte Expansion¶
(optional)
echo $greetings
Hallo Welt!
andere_variable="greetings" # Speichere den Variablennamen "greetings" in einer Varaiblen
echo ${andere_variable}
echo ${!andere_variable} # indirekte Expansion
greetings Hallo Welt!
Default Values¶
echo $foo # Hier wird nichts angezeigt, da foo nicht gesetzt
echo ${foo:-"Wenn Variable 'foo' nicht gesetzt, wird dies verwendet."}
Wenn Variable 'foo' nicht gesetzt, wird dies verwendet.
Übung¶
Beantworten Sie folgende Fragen:
- Wie kann man einfach aus der Variable
Var="Ich bin die Königin der Kommandozeile."
eine neue VariableVar1
erstellen, wobei dasbin die Königin der
durchbeherrsche die
ersetzt wurde. - Wie kann man aus der Variablen
Var
die Zeichen 19 bis zum Ende ausschneiden? - Wie kann man aus der Variablen
Var
die Zeichen 19 bis vorletzten Zeichen ausschneiden, d.h. nicht en abschließenden Punkt?
Var="Ich bin die Königin der Kommandozeile."
Substitutions¶
Subshells¶
Subshells werden als Kopien (der Umgebung) von der ursprünglichen Shell gestartet (als Subprozess) - mehr zu Subshells (und Prozessen) später.
Hinweis: Variablen die in der Subshell erzeugt werden, sind in der aufrufenden Shell nicht verfügbar. Da Subshells auch beim Pipen erzeugt werden, kann dies zu subtilen Fehlern führen (siehe unten).
Runde Klammern zur Erzeugung von Subshells mit und ohne dem $
-Zeichen¶
$(...)
Das Kommando innerhalb der Klammern wird in einer Subshell ausgeführt und stdout des Kommandos als Rückgabewert erhalten. Das wird auch als command substitution bezeichnet.(...)
Das Kommando innerhalb der Klammern wird in einer Subshell ausgeführt (ohne Rückgabewert).
In beiden Fällen werden also mit den runden Klammern Subshells erzeugt.
Command Substitution¶
$(kommando)
Das Kommando innerhalb der Klammern wird in einer Subshell ausgeführt und stdout des Kommandos als Rückgabewert erhalten. Das wird auch als command substitution bezeichnet.
Beispiel:
ls -l $(which ping)
# alternativ zu
# which ping | xargs ls -la
-rwxr-xr-x 1 root root 76672 Feb 5 2022 /usr/bin/ping
Alternative Syntax für Command Substitution per Backticks:
ls -la `which ping`
-rwxr-xr-x 1 root root 76672 Feb 5 2022 /usr/bin/ping
echo "The current date is $(date)"
# alternative mit backticks "`"
echo "The current date is `date`"
The current date is Mo 4. Dez 18:07:35 CET 2023 The current date is Mo 4. Dez 18:07:35 CET 2023
Kommandos in Gruppe¶
Bei Kommando innerhalb geschweifter Klammern:
{...}
Führt die Kommandos in den geschweiften Klammern als Gruppe aus.
Dabei muss immer ein Leerzeichen zwischen den geschweiften Klammern und den Befehlen darin stehen. Außerdem müssen alle Befehle mit einem ;
abgeschlossen werden.
So lassen sich bespielsweise alle Ausgaben einer Kette von Kommandos gemeinsam in eine Datei umleiten:
{ echo "I found all these PNGs:"; find . -iname "*.png"; echo "Within this bunch of files:"; ls; } > PNGs.txt
Beispiel aus All-about curly braces in Bash.
Hier geben wir uns zum Vergleich mit der (speziellen) Variablen BASHPID
die Prozess-ID der bash-Shell aus. Beachten Sie, dass diese sich für die Subshell-Variante von der ausrufenden Shell unterscheidet.
Beachten Sie auch die Sichtbarkeit von Variablen, die in einer Subshell bzw. aufrufenden Shell gesetzt werden.
echo direkt
a=9 # setze die Variable in der Shell
echo $(echo "a ist" $a) # in der Subshell ist die Variable verfügbar
echo bash-PID:$BASHPID
echo -------------------------
echo Subshell
(b=10, echo bash-PID:$BASHPID) # setze die Variable in einer Subshell
# in der aufrufenden Shell ist die Variable nicht gesetzt!
echo b ist $b
echo -------------------------
echo Gruppe
{ c=119; echo bash-PID:$BASHPID; }
# in der "aufrufenden" Shell ist die Variable gesetzt!
echo c its $c
direkt a ist 9 bash-PID:92746 ------------------------- Subshell bash-PID:93164 b ist ------------------------- Gruppe bash-PID:92746 c its 119
# das gilt natürlich auch für Änderungen von Variablen
a=1; (a=2; echo "inside: a=$a"); echo "outside: a=$a"
inside: a=2 outside: a=1
Aufgabe¶
Wie kann man (einfach) den Inhalt einer Datei in eine Variable einlesen?
Aufgabe¶
Wie kann man etliche Befehle ausführen und danach wieder im Ursprungsverzechnis sein? z.B.
cd ~ ; cat *.txt ; ls -l ; cd my_dir1; cat *.txt
Probieren Sie es einfach aus. Hilft Gruppieren oder eine Subshell?
Aufgabe¶
Was ist der Unterschied zwischen Kommandos, die in runden ( .. )
und geschweiften Klammern { .. }
ausgeführt werden?
Hier finden Sie Unterschiede kurz beschrieben:
https://stackoverflow.com/questions/31255699/double-parenthesis-with-and-without-dollar
Process Substitution¶
<( kommando )
erzeugt eine anonyme named pipe und verbindet stdout des Kommandos mit der named pipe. An der Stelle wird implizit der Filedescriptor der named pipe zurückgegeben. Das ist sinnvoll für andere Kommandos denen typischerweise ein Dateinamen übergeben wird.
# cat bekommt einen Dateinamen als Input
# Dies demonstriert das Prinzip der process substitution
cat <(echo das landet in der Datei)
das landet in der Datei
Das ist effektiv das Gleiche wie:
# hier liest cat von stdin
echo das landet in der Datei | cat
das landet in der Datei
# hier wird der Dateiname (fd - file descriptor) ausgegeben
# dies ist in der Regel uninteressant - hier nur zur Demonstration
echo <( echo Hallo )
/dev/fd/63
Aber es sind bei Process Substitution auch mehrere Kommandos möglich:
cat <(echo bar; echo foo)
# mittels ";" können mehrere Befehle in eine Zeile geschrieben werden
# Diese werden dann hintereinander ausgeführt.
bar foo
Anwendungsbeispiel für Process Substitution¶
Wir wollen die von wc
zurückgelieferten Informationen in Variablen speichern:
# Erzeuge die Datei greetings im laufenden Arbeitsverzeichnis
echo "Hallo Welt
Hello World
Hola mundo" > greetings
# Ein sinnvolles Beispiel
# greetings ist eine Textdatei!
# und wc zählt die Zeilen, Wörten und Zeichen
read lines words chars _ < <(wc greetings)
# lines words und chars sind Variablen (später mehr dazu)
# um den Inhalt von Variablen zu erhalten benötigt man das "$"
# Mehr dazu später
echo $lines
echo $words
echo $chars
3 6 34
Ausführliche Erklärung:
# zur Demonstration von wordcount-programm "wc"
# mehr zu wc siehe z.B. "man wc"
wc greetings
3 6 34 greetings
Erklärung von read lines words chars _ < <(wc greetings)
:
read
- Liest eine Zeile vom Standardinput und teilt diese in "Felder" - (siehehelp read
).<(wc greetings)
erzeugt mittels Process substitution eine named pipe, d.h. eine temporäre Datei.- Das erste
<
leitet den Inhalt der named pipe-temporären Datei zuread
um.
Here-Strings und Here-Dokumente¶
Here Documents und Here Strings werden an Stelle einer Datei eingesetzt.
Here Documents und Here Strings werden in bash mittels temporärer Dateien realisiert.
Mehr dazu siehe z.B.: https://askubuntu.com/questions/678915/whats-the-difference-between-and-in-bash
Beispiel für ein Here Document (<<
).
wc << EOF
Das ist ein
Dokument über mehrere Zeilen
EOF
Die Zeichenkette (hier EOF
), die das Ende des mehrzeiligen Dokumentes angibt, kann beliebig gewählt werden.
wc << EOF
Das ist ein
Dokument über mehrere Zeilen
EOF
2 7 42
# bc ist ein eigentlich ein interaktiver Calculator
# mehr siehe `man bc`
bc << End
a=3
b=5
a*b
End
15
# Demonstration des Dokumentes, das bc in obigem Beispiel erhält:
cat << End
a=3
b=5
a*b
End
a=3 b=5 a*b
Beipiel für ein here string (<<<
)
bc <<< 4*5
20
cat <<< 4*5
4*5
Eine typische Anwendung von Here Documents (und Here Strings) ist auf einem entfernten Rechner (per ssh
oder sftp
) mehrere Kommandos auszuführen, die in ein Here Document geschrieben werden. ssh
wird später im Kurs behandelt.
Weitere Anwendung: Einen anderen Interpreter, z.B. den Python-Interpreter, aus der bash mit Befehlen aufrufen:
# Python Interpreter aufrufen mit "Hallo Welt"-Programm
python <<< "print ('Hallo Welt')"
# ist deutlich einfacher als die Alternative mit Process Substitution und stdout-Umleitung
python < <(echo "print('Hallo Welt')")
Hallo Welt Hallo Welt
# mit einem here-Dokument
python << BOF
print ('Hallo Welt,')
print ('sagt das mehrzeilige Python Programm.')
BOF
Hallo Welt, sagt das mehrzeilige Python Programm.
weitere Übungen¶
Aufgabe¶
Wie löschen Sie das poetry-Verzeichnis inkl. aller Unterverzeichnisse und Dateien auf der Kommandozeile (vgl. letzte Übung).
Aufgabe¶
Mit Hilfe des Programms du
(disc usage) können Sie sich anzeigen lassen, wieviel
Speicher Verzeichnisse (inkl. Unterverzeichnisse) belegen, siehe man du
.
Wie kann man alle Verzeichnisse direkt unter dem eigenen Heimverzeichnis erhalten (inkl. Speicherverbrauch), die mehr als ein Gigabyte verbrauchen?
Hinweis: du
mit Schalter -h
und --max-depth
in Kombination mit grep
.
Aufgabe¶
Kopieren Sie alle regulären Dateien im Downloadordner (~/Downloads/
), die in den letzen 60 Minuten modifiziert worden sind, in den Order ~/data/gene-expression/
.
Passen Sie die Aufgabe an, d.h. wählen Sie geeignete Dateien und Verzeichnisse, die bei Ihnen vorhanden sind.
Aufgabe¶
Kopieren Sie alle pdf- und png-Dateien (Endungen pdf
bzw. png
case-insensitive) des laufenden Arbeitsverzeichnis, die jünger als der 8.März sind, in das Verzeichnis (./rechungen
).
Passen Sie die Aufgabe an, d.h. wählen Sie geeignete Dateien und Verzeichnisse, die bei Ihnen vorhanden sind.
Aufgabe¶
Gegeben ist ein beliebiger relativer (oder absoluter) Pfad zu einer Datei, z.B.
./src/poetry/config/config.py
. Bestimmen Sie hieraus den Namen der Datei
und den absoluten Pfad der Datei und speichern Sie diese in zwei Variablen.
Der absolute Pfad sollte keinen .
oder ..
enthalten.
Hinweis: Benutzen Sie (unter anderem) die Posix-konformen Kommandos:
basename
dirname
cd ~/tmp/poetry
bash: cd: /home/christian/tmp/poetry: No such file or directory
# Ihre Lösung sollte mit einem beliebigen relativen
# oder auch absoluten folgendem Pfad zurecht kommen, wie z.B.
myfile=./src/poetry/config/config.py
echo Der Name der Datei ist $mybasename
echo Der absolute Pfad zur Datei ist $myabsolutepath
Der Name der Datei ist config.py Der absolute Pfad zur Datei ist
Lernkontrollfragen¶
Folgende Fragen sollten Sie (im Groben) ohne Nachschlagen beantworten können. Details wie spezielle Schalter etc. müssen Sie dabei nicht auswendig können:
- Mit welchem Befehl können Sie Dateien finden?
- Wie können Sie auf den gefundenen Dateien Operationen (wie Löschen) automatisch ausführen?
- Sind reguläre Ausdrücke das gleiche wie Globbing? Ist die Syntax gleich?
- Was sind reguläre Ausdrücke? Was kann man damit machen?
- Sie sollten diese auf Problemstellungen (mit eventuellem Nachschlagen der konkreten Syntax) entwickeln können.
- Wie lautet der Befehl mit dem Sie in Textdateien mit regulären Ausdrücken "suchen" können. Worauf trifft ein Muster dabei zu oder nicht?
- Wie setzt man den Wert einer Variable und wie erhält man diesen später?
- Was sind Subshells?
- Wozu dient
$(...)
und(...)
und was ist dabei der Unterschied zwischen den beiden Varianten? MIt welchem Begriff wird$(...)
bezeichnet? - Wozu dient
<(...)
? Wie heißt das? - Was sind Here Strings und Here Documents?
Literatur¶
Allgemein¶
- Machtelt Garrels, Bash Guide for Beginners
- Bash Reference Manual
Quoting¶
https://www.gnu.org/software/bash/manual/html_node/Quoting.html