Der ASCII-Code (American Standard Code for Information Interchange) ist ein (alter) Standard um Zeichen zu kodieren. ASCII beschreibt einen Sieben-Bit-Code. D.h. es können $2^7 = 128$ Zeichen mit ASCII-Code dargestellt werden. Steuerzeichen (wie z.B.: Zeilenvorschub) sind im ASCII-Code ebenfalls erhalten.
Beispiel: Buchstabe A : 1000001
Durch Hinzunahme des 8-ten Bit (des Bytes) können nochmal 128 Zeichen dargestellt werden. Diese können für länderspezifische Zeichen genutzt werden, wie z.B. ISO 8859-1 (Latin-1) mit deutschen Umlauten. In unterschiedlichen Ländern sind unterschiedliche Standards üblich. Dies führt zu Inkompatibilitäten zwischen den verschiedenen Ländercodierungen bzw. Computersystemen (z.B. bei deutschen Umlauten).
Unicode ist ein internationaler Standard, mit dem Ziel spezielle Zeichen aller Sprachen (Schriftkulturen) zu umfassen. Unicode weist jedem Zeichen eine eindeutige Nummer (Unicode-Wert) zu. Durch standardisierte Kodierungen der Unicode-Werte, wie UTF-8, sollen die Inkompatibilitäten (falsche Darstellungen) vermieden werden.
Für den Unterschied zwischen Unicode und Implementierungen wie UTF-8, siehe z.B. https://stackoverflow.com/questions/643694/what-is-the-difference-between-utf-8-and-unicode
Text-Daten können als Dateien (Files) im ASCII-Code oder UTF-8 auf einem Rechner gespeichert werden. Diese Dateien können ohne größeren Schwierigkeiten zwischen verschiedenen Computersystemen ausgetauscht werden und dort angezeigt und weiterverarbeitet werden. Dabei ist darauf zu achten, dass die Kodierung richtig erkannt bzw. eingestellt wird. Desweiteren können jedoch Probleme bei der Handhabung des Zeilenendes auftreten. Windows verwendet zur Markierung des Zeilenendes die Kombination Wagenrücklauf-Zeilenvorschub (carriage return - line feed, CRLF). Unix und Linux nutzen nur den Zeilenvorschub (line feed, LF).
Beispiele für Text-Dateien: CSV, Emails, HTML-Webseiten.
file
command¶Das file
Kommando führt mehrere Tests durch, um den Typ einer Datei zu klassifizieren. Dabei wird auch
versucht die Kodierung von Textdateien genauer zu bestimmen. Mehr siehe man file
.
file countries.txt
echo
file ~/bin/backup.sh
countries.txt: ASCII text /home/chris/bin/backup.sh: Bourne-Again shell script, ASCII text executable
file ../regEx/regular-expression-exercise.ipynb
echo
file /home/chris/tmp/side_by_side_without_boarder_nearest.png
../regEx/regular-expression-exercise.ipynb: UTF-8 Unicode text /home/chris/tmp/side_by_side_without_boarder_nearest.png: PNG image data, 1920 x 1080, 8-bit/color RGBA, non-interlaced
Gängige Text-Editoren für die Kommandozeile sind:
emacs
und drücken Sie STRG&h und danach t.-nw
(no window), startet emacs im Terminal(-Fenster). Wählen Sie einen Kommandozeilen-Text-Editor aus und arbeiten Sie sich in diesen ein. Recherchieren Sie hierzu ein geeignetes Tutorial.
Shell-Skripte sind Programme gespeichert in Text-Dateien, die von der Shell(-Umgebung) ausgeführt werden können. Shell-Skripte werden interpretiert, nicht kompiliert. D.h. während der Ausführung liest der Interpreter das Skript Zeile für Zeile und führt diesen so aus. Ein Kompiler dagegen übersetzt ein Programm in ausführbaren Maschinencode, d.h. in eine Form, die von der Computer-Hardware direkt ausgeführt werden kann (oder von einer virtuelle Maschine, wie bei Java).
Vorteile von Shell Skripen:
Nachteile:
Für komplexere Skripte eignen daher sich andere Skriptsprachen, wie Python, deutlich besser.
Shell-Skripe bestehen im einfachsten Fall aus einer Folge von Befehlen. Aber es sind auch Kontrollstrukturen (Schleifen, Bedingungen usw.) und Strukturierungen durch Funktionen etc. möglich.
Beachten Sie, dass es aber gute Praxis ist, den Shebang mit dem env
-Kommando zu verwenden.
Mit env
läuft ein Programm in einer modifierten Umgebung. Typischerweise liegt env
im Verzeichnis /usr/bin/
, sodass folgende Beispiele für den Shebang in der ersten Zeile eines Skriptes sinnvoll sind:
#!/usr/bin/env bash
- Bash-Skripte#!/usr/bin/env sh
- POSIX konforme Shell-Skripte#!/usr/bin/env python
- Python Skripteenv
verwendet die PATH
Umgebungsvariable (siehe unten), um den passenden Interpreter (genauer: das Interpreterprogramm) zu finden.
Beispielskript "Hallo Welt":
# Erzeugen des Skriptes und versehen mit Ausführrechten
echo '#!/usr/bin/env bash
echo Hallo Welt!' > hw.sh
chmod u+x hw.sh
./hw.sh
Hallo Welt!
Shell-Skripte haben oft die Endung .sh
. Dies ist aber optional.
Folgende spezielle Variablen gibt es in Bash bzw. Bash-Skripten
$0
Aufruf Name des Skripts (inkl. Pfad)$1
bis $9
sind Argumente des Skripts oder der Funktion (beim Aufruf übergeben).$@
sind alle Argumente als Liste (Listen/Arrays werden weiter unten behandelt)$#
Anzahl der Argumente$?
- Rückgabecode des vorherigen Kommandos$$
- Prozeß-ID (PID) des laufenden Skiptes (siehe unten)!!
- Vollständiges letztes Kommando inkl. der Argumente. Eine gängige Anwendung ist, falls das letze Kommando aufgrund von fehlenen root-Rechten nicht ausgeführt werden konnte. So können Sie es mit dem Kürzel sudo !!
mit root-Rechten ausführen. $_
- Letztes Argument des letzten Kommandos. In einer interaktiven Shell bekommt man dies auch über die Tastenkombination Esc gefolgt von ..$!
beinhaltet die Prozess ID, des zuletzt im Hintergrund ausgeführten Prozesses. Beispiele:
wc not-present 2>/dev/null # fehlermeldung wird unterdrückt
echo The exit code of last command is: $?
The exit code of last command is: 1
Beachten Sie, dass Fehlermeldungen von Kommandos (Programmen) mit Zahlen codiert werden. Dabei steht die Null für "Kein-Fehler":
ls >/dev/null
echo The exit code of last command is: $?
The exit code of last command is: 0
sleep 3 & # & bewirkt Ausführung im Hintergrund
echo $!
# Hier werden Prozess-IDs ausgegeben - siehe unten
[1] 10605 10605
# Hinweis: Wir müssen die $-Zeichen im here-Document escapen
# damit die bash keine Variablen-Substitution vornimmt!
cat > example_script.sh << EOF
#!/usr/bin/env bash
echo Der Name diese Skripts mit Pfad beim Aufruf: \${0}
echo Mit folgendem ersten Argument: \${1}
echo Das zweite Argument ist: \${2}
echo Die Anzahl aller Argumente ist \$#
EOF
chmod u+x example_script.sh # ausführbar machen!
# Ausführen des Skriptes
./example_script.sh foo bar
Der Name diese Skripts mit Pfad beim Aufruf: ./example_script.sh Mit folgendem ersten Argument: foo Das zweite Argument ist: bar Die Anzahl aller Argumente ist 2
cat example_script.sh
#!/usr/bin/env bash echo Der Name diese Skripts mit Pfad beim Aufruf: ${0} echo Mit folgendem ersten Argument: ${1} echo Das zweite Argument ist: ${2} echo Die Anzahl aller Argumente ist $#
Hinweis: Prozesse werden später ausführlicher behandelt.
# So erhält man die PID des Bash-Prozesses der Shell
echo -e "shell:\n" '$='$$ 'BASHPID='$BASHPID
shell: $=23432 BASHPID=23432
Beachte (aus https://unix.stackexchange.com/questions/484442/how-can-i-get-the-pid-of-a-subshell)
$$
: Expands to the process ID of the shell. In a subshell, it expands to the process ID of the current shell, not the subshell.BASHPID
: Expands to the process ID of the current bash process. This differs from $$
under certain circumstances, such as subshells that do not require bash to be re-initialized.#subshells
echo -e $(echo "subshell\n" '$='$$ 'BASHPID='$BASHPID)
subshell $=23432 BASHPID=4659
Wenn ein Shell-Skript ausgeführt wird, wird ein neuer Prozess gestartet. Die aufrufende Shell ist der Eltern-Prozess (parent process) und der Prozess des Shell-Skriptes der Kind-Prozess (child process).
# Auch der Shell-Prozess hat ein Parent
# So erhält man die PID des Parent, die PPID
echo $PPID
12744
Umgebungsvariable (Environment Variables) sind besondere Variablen, die von Prozessen für unterschiedliche Dinge verwendet werden können (Beispiele später). Umgebungsvariablen werden bei der Prozess-Erzeugung an die Kinderprozesse vererbt. Die Kinderprozesse erhalten eine Kopie der Umgebung des Elternprozesses.
Das Kommando printenv
dient dazu die gesetzen Umgebungsvariablen anzuzeigen.
printenv | grep -i python
VIRTUAL_ENV=/home/chris/python-virtal-envs/py3.8.0 PATH=/home/chris/python-virtal-envs/py3.8.0/bin:/home/chris/python-virtal-envs/py3.8.0/bin:/home/chris/.local/bin:/home/chris/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
Mit dem Kommando printenv
können Sie direkt die Werte von Umgebungsvariablen erhalten, z.B.
printenv VIRTUAL_ENV # zeigt nur die Variable VIRTUAL_ENV an!
echo
#die Werte von VIRTUAL_ENV sind natürlich auch per Parameter-Substitution erhältlich
echo $VIRTUAL_ENV
/home/chris/python-virtal-envs/py3.8.0 /home/chris/python-virtal-envs/py3.8.0
Man kann die Kommandozeile als Umgebung sehen, die shell-Code ausführt. Kommandos sind
dann shell build-ins oder weitere Programme, die ausgeführt werden können, d.h. in der Regel weitere Prozesse ergeben.
Diese Programme, d.h. die ausführbaren Dateien, muss die shell finden. Dazu dient die Umgebungsvariable PATH
.
So kann man sich anzeigen lassen, wo die shell nach den ausführbaren Programmen sucht:
printenv PATH
/home/chris/python-virtal-envs/py3.8.0/bin:/home/chris/python-virtal-envs/py3.8.0/bin:/home/chris/.local/bin:/home/chris/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
In der Variablen SHELL
ist die Art des Shell-Interpreters (der laufenden Shell) gespeichert:
printenv SHELL
/bin/bash
export
¶Shell Skripte laufen in einem eigenen Prozess (erkennbar an der anderen PID).
Wenn in der aufrufenden Shell eine Variable gesetzt wird, dann ist diese
im Shell Skript nicht verfügbar (anderes als bei Subshells via $(...)
oder Pipes). Das Skript muss ja kein Bash-Skript sein und Bash-Skripte werden hier nicht besonderes behandelt.
Variablen, die mit export
gesetzt werden, sind dagegen (in den Child-Prozessen und somit auch) im Skript-Prozess gesetzt.
echo PID der aufrufenden Shell: $$
my_normal_var=10
export my_env_var=11
echo '#!/bin/bash
echo Die PID dieses Skriptes ist $$
echo Der Parent-Prozess des Skriptes hat die PID $PPID # $(ps -o ppid= $$)
echo my_normal_var ist nicht sichtbar: $my_normal_var
echo my_env_var dagegen schon: $my_env_var
' > example_script.sh
chmod u+x example_script.sh
./example_script.sh
PID der aufrufenden Shell: 12753 Die PID dieses Skriptes ist 13716 Der Parent-Prozess des Skriptes hat die PID 12753 my_normal_var ist nicht sichtbar: my_env_var dagegen schon: 11
Mit export
setzt man also Variablen, die in allen Child-Prozessen des
Parent-Prozess (hier der Shell) verfügbar sind.
Diese Variablen sind somit in der Umgebung gesetzt. Dies sieht man auch mittels:
env | grep my_env_var
my_env_var=11
Passen Sie den Prompt (Umgebungsvaribale PS1
) nach Ihrem Geschmack an. Wie dies geht, finden Sie hier:
https://wiki.ubuntuusers.de/Bash/Prompt/
Hilfreich sind dabei auch
Konfigurationsdateien erlauben das Verhalten der Shell zu konfigurieren. Die wichtigsten sind
.bashrc
und .bash_profile
; siehe z.B. https://wiki.ubuntuusers.de/Bash/bashrc/
.bash_profile
wird bei login Shells (beim Start der Shell) ausgeführt..bashrc
wird bei interaktiven non-login Shells ausgeführt."Will man also bei einer Login-Shell etwas einmalig ausführen, ist die bash_profile
der richtige Ort. Will man, dass die Veränderungen bei jedem Start eines (pseudo-)Terminals oder einer Konsole ausgeführt werden, ist die bashrc
der richtige Ort." aus https://wiki.ubuntuusers.de/Bash/bashrc/
Wollen Sie z.B. die PATH
-Umgebungsvariable erweitern, so sollte bash_profile
die richtige Wahl sein, siehe auch https://linuxize.com/post/bashrc-vs-bash-profile/
Mehr zu Bash-Startup-Files finden Sie im gnu bash User-Manual unter dem Punkt 6.2 Bash Startup Files.
Hinweis: Die Dateien mit einem Punkt als erstes Zeichen im Namen sind sogenannte versteckte Dateien.
Diese werden standardmäßig nicht angezeigt, z.B. ist bei ls
die Option -a
nötig, um diese auch
aufzulisten.
Den Standardeditor können Sie mit Hilfe der Umgebungsvariablen EDITOR
setzen.
Am besten setzt man dies in der Konfigurationsdatei .bashrc
.
Setzen Sie Ihren Lieblingseditor in der .bashrc
. Wie lautet die entsprechende Befehlszeile?
Beim Editor nano, z.B.:
export EDITOR=/usr/bin/nano
Setzen Sie die Sprache der Bash (z.B. zur besseren Internetrecherche bei Fehlermeldungen etc.) auf amerikanisches Englisch. In der Datei .bashrc
müssen Sie hierzu die Variable LANG
auf en_US.UTF-8
setzen (export
nicht vergessen!).
Mittels des Alias-Mechanismus können Sie sich Abkürzungen von Befehlen (inkl. Argumenten) erstellen.
Mit dem Befehl alias
(ohne Argumente) bekommen Sie die definierten Aliase angezeigt:
alias
alias alert='notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"' alias egrep='egrep --color=auto' alias fgrep='fgrep --color=auto' alias grep='grep --color=auto' alias l='ls -CF' alias la='ls -A' alias ll='ls -alF' alias ls='ls --color=auto' alias openboard='flatpak run ch.openboard.OpenBoard'
Ein (etwas sinnloses) Beispiel für die Definition eines Aliases:
# myls ist der Alias für "ls -lrth *.txt"
alias myls_txt='ls -lrth *.txt'
# Jetzt kann der Alias verwendet werden:
myls_txt
-rw-r--r-- 1 chris chris 5 Feb 5 2021 f2.txt
-rw-r--r-- 1 chris chris 5 Feb 5 2021 f.txt
-rw-r--r-- 1 chris chris 7,8K Feb 6 2021 t.txt
-rw-r--r-- 1 chris chris 462 Mär 18 2021 ls-out.txt
-rw-r--r-- 1 chris chris 70 Apr 15 2021 rechnernamen.txt
-rw-r--r-- 1 chris chris 100 Apr 15 2021 countries_2.txt
-rw-r--r-- 1 chris chris 96 Apr 15 2021 countries_3.txt
-rw-r--r-- 1 chris chris 100 Apr 15 2021 countries_4.txt
-rw-r--r-- 1 chris chris 17K Okt 12 14:39 mylog.txt
-rw-rw-r-- 1 chris chris 23 Nov 2 14:29 countries_1.txt
lrwxrwxrwx 1 chris chris 13 Nov 3 08:21 countries_soft_link.txt -> countries.txt
-rw-rw-r-- 2 chris chris 79 Nov 3 14:54 countries.txt
-rw-rw-r-- 2 chris chris 79 Nov 3 14:54 countries_second_hard_link.txt
Typischerweise werden die Aliase in der regulären Datei ~/.bash_aliases
definiert, sodass
die Aliase dann beim Öffnen der Kommandozeile verfügbar sind.
Dazu wird typischerweise diese Aliasdatei automatisch von einem bash-Konfigurationsskript (siehe unten)
ausgeführt mittels source ~/.bash_aliases
; zum Befehl (shell buildin) source
(Abkürzung .
) siehe help source
oder help .
.
mehr zum Alias-Mechanismus siehe https://de.wikibooks.org/wiki/Linux-Praxisbuch/_Bourne_Again_Shell#Der_Alias-Mechanismus
Im Folgenden werden kurz Funktionen, (assoziative) Arrays und Kontrollstrukturen für Shell-Skripte vorgestellt. Damit ist es möglich richtige Programme in bash zu entwickeln.
Alternativ können Sie eines der vielen Tutorials im World Wide Web mit einer Suchmaschine Ihrer Wahl finden und durcharbeiten.
Die Struktur einer Funktion ist folgende:
funktionsname () {
kommando_1
kommando_2
...
}
Für die Argumente gilt das gleiche wie bei den Skripten, d.h. es gibt $#
, $1
, etc.
zum Beispiel (Funktionen kann man auch direkt auf der Kommandozeile definieren):
# Eine sehr einfache Funktion
function get_the_meaning_of_live () {
echo 42
}
# Aufruf der Funktion mit dem Namen der Funktion:
get_the_meaning_of_live
42
# Bei Funktionen kann man auch explizit das Schlüsselwort "function" angeben:
function silly_example () {
echo "Das ausführende 'Programm' bzw. Skript heißt $0"
echo "Zahl der Argumente $#"
echo "Erstes Argument $1"
}
silly_example bla blub
das ausführende 'Programm' bzw. Skript heißt /bin/bash Zahl der Argumente 2 erstes Argument bla
my_array=("first" "second" "third")
# oder bei dem Inhalte aus einer Variablen mit Leerzeichen im Inhalt
#my_array=($var)
# die Zählung beginnt bei 0
echo drittes Element: "${my_array[2]}"
echo Anzahl der Elemente "${#my_array[@]}"
drittes Element: third Anzahl der Elemente 3
# Man kann auch rückwärts indizieren:
echo "${my_array[-3]}"
first
# Alle Elemente ausgeben:
echo ${my_array[@]}
first second third
Assoziative Arrays werden mittels declare -A name
erzeugt.
Mittels assoziative Arrays kann ein Schlüssel (key) auf einen Wert (value) abgebildet werden:
declare -A capital_city
capital_city["Frankreich"]="Paris"
capital_city["Spanien"]="Madrid"
capital_city["Deutschland"]="Berlin"
echo ${capital_city["Spanien"]}
echo
# all keys - note the order is undefined!
echo ${!capital_city[@]}
echo
# can be used in a loop
for k in ${!capital_city[@]}; do
echo "${capital_city[$k]} ist die Hauptstadt von ${k}."
done
Madrid Deutschland Frankreich Spanien Berlin ist die Hauptstadt von Deutschland. Paris ist die Hauptstadt von Frankreich. Madrid ist die Hauptstadt von Spanien.
Mehr zu Arrays siehe Bash Gnu Manual.
Schreiben Sie eine shell-Funktion denen Sie zwei Argumente übergeben:
cd
s als Kommando), soll
nach Beenden der Funktion das ursprüngliche Arbeitsverzeichnis wieder gelten. Wie kann das einfach realisiert werden? Beispiel:
comment_and_execute "Das wird bei ls -l ausgegeben:" "ls -l"
Das wird bei ls -l ausgegeben:
total 572
drwxr-.....
.....
Hinweis: Befehl eval
comment_and_execute 'Das wird bei cd ~ und ls ausgegeben' 'cd ~ ; ls'
Das wird bei cd ~ und ls ausgegeben backup Downloads lehre-extern Public bin encrypted models python-virtal-envs computational-graph.pdf examples.desktop Music snap data git-repos notebooks Templates dead.letter go Pictures tmp deep.TEACHING HTW-nextcloud private Videos Desktop HTW-sensibel programs virtual-machines
Mit Tests kann man überprüfen, ob Variablen gesetzt oder leer sind bzw. sind Werte- und Stringvergleiche möglich. Dateien können auch überprüft werdem, z.B. ob sie vorhanden sind bzw. welchen Typen sie haben.
Die Tests werten zu Wahr (true
) oder Falsch (false
) aus.
So können damit auch Programm-Abzweigungen oder bedingte Ausführungen realisiert werden.
&&
: Und-Operator (And)||
: Oder-Operator (Or)!
: NegationBeide werden bedingt ausgewertet (Kurzschlussauswertung, short-circuit evaluation):
true || echo wird nicht geschrieben
false && echo wird nicht geschrieben
# ist das gleiche wie
! true && echo wird nicht geschrieben
true && echo wird geschrieben
false || echo wird geschrieben
wird geschrieben wird geschrieben
a='Hallo Welt'
test "$a" || echo Variable ist leer oder nicht gesetzt!
true || echo Variable ist leer oder nicht gesetzt!
Typischerweise werden für Tests aber eckige Klammern verwendet: [ ]
.
Die einfachen eckigen Klammern sind eine Referenz auf test
.
[ "$a" ] || echo Variable ist leer oder nicht gesetzt!
In Bash (nicht shell POSIX-konform) gibt es auch doppelte eckige Klammern [[
:
[[ $a ]] || echo Variable ist leer oder nicht gesetzt!
-z
kann überprüft werden, ob die Variable leer/nicht-gesetzt ist.
Liefert im Fall leer/nicht-gesetzt true
zurück.-n
kann überprüft werden, ob die Variable nicht-leer/gesetzt ist.
Liefert im Fall nicht-leer/gesetzt true
zurück.Für eine Liste der Vergleichsoperatoren siehe man test
oder z.B. https://tldp.org/LDP/abs/html/comparison-ops.html
a="a"
unset a
# note: the test is true! "And"-Chain
[[ -z $a ]] && echo Variable ist leer oder nicht gesetzt!
# dasgleiche wie
[[ ! $a ]] && echo Variable ist leer oder nicht gesetzt!
# note: the test is false! "Or"-Chain
[[ -n $a ]] || echo Variable ist leer oder nicht gesetzt!
# dasgleiche wie
[[ $a ]] || echo Variable ist leer oder nicht gesetzt!
Variable ist leer oder nicht gesetzt! Variable ist leer oder nicht gesetzt! Variable ist leer oder nicht gesetzt! Variable ist leer oder nicht gesetzt!
b="$a"
#b="Hola Mundo"
# = ist hier ein Vergleich
[[ $a = $b ]] && echo beide Variablen sind gleich.
# es geht aber auch ==
[[ $a == $b ]] && echo beide Variablen sind gleich.
[[ $a != $b ]] && echo beide Variablen sind ungleich.
beide Variablen sind gleich. beide Variablen sind gleich.
Vergleiche von Variablen, die Ganzzahlen enthalten, werden folgendermaßen vorgenommen:
# Beachten Sie die Ausgabe.
a=3
b=5
[[ $a -eq $b ]] && echo a gleich b
[[ $a -ne $b ]] && echo a ungleich b
[[ $a -gt $b ]] && echo a größer b
[[ $a -lt $b ]] && echo a kleiner b
[[ $a -ge $b ]] && echo a größer-gleich b
[[ $a -le $b ]] && echo a kleiner-gleich b
# Es geht aber auch mit einer arithmetischen Umgebung (siehe unten)
[[ (($a < $b )) ]] && echo a kleiner b
[[ (($a > $b )) ]] && echo a größer b
a ungleich b a kleiner b a kleiner-gleich b a kleiner b
Da man die Shell oft nutzt, um Dateien zu maniplieren, existieren etliche Vergeleiche für Dateien, wie z.B.
-e
Datei existiert-f
ist eine reguläre Datei-s
Datei hat nicht Größe Null-d
Datei ist ein Verzeichnis [[ -e "./countries.txt" ]] && echo "countries.txt existiert im Arbeitsverzeichnis"
[[ -d "." ]] && echo "Das Arbeitsverzeichnis '.' ist wenig überraschend ein Verzeichnis."
countries.txt existiert im Arbeitsverzeichnis Das Arbeitsverzeichnis '.' ist wenig überraschend ein Verzeichnis.
# example from https://program-script.com/bash/control-structures/if-then-else.php
function if_then_else(){
number=$1
if [ $number -gt 0 ]; then
echo "Zahl ist positiv."
elif [ $number -lt 0 ]; then
echo "Zahl ist negativ."
else
echo "Zahl ist Null."
fi
}
if_then_else -1
Zahl ist negativ.
# simple regEx
VAR="Tesus"
if [[ $VAR =~ Th?esus ]] # =~ regex-check
then
echo match
fi
match
Es verschiedene Möglichkeiten arithmetische Berechnungen durchzuführen, wie expr
, let
, declare
und arithmetic Expansions. Dabei ist letzteres für einfache ganzzahlige Operatoren zu bevorzugen:
$((...))
Arithmetische Expansion mit eventueller Änderung der Shell-Variablen mit Rückgabewert.((...))
Compound Command für arithmetische Ausdrücke (arithmetic expressions), d.h. arithmetische Berechnungen mit eventueller Änderung der Shell-Variablen (ohne Rückgabewert).echo 3 + 4 ist $((3+4))
3 + 4 ist 7
a=5
((a++)) # Beachte: kein $ vor der Variablen a
echo $a
((a*=2))
echo $a
6 12
((a=4+3)); echo $a
echo $((a=4+4)) ist $a
7 8 ist 8
z=1
while [[ $z -le 5 ]]
do
echo $z
((z++))
done
1 2 3 4 5
z=1
until [ $z -gt 5 ]
do
echo $z
((z++))
done
1 2 3 4 5
break
- Loop verlassencontinue
- Beende laufende Iteration# ein ziemlich überkompliziertes Beispiel
# in sehr schlechtem Stil
z=0
while true
do
[[ $z -eq 10 ]] && break
((z++))
if (($z % 2)); then
continue # das ist nur zur Demonstration von continue und so nicht sinnvoll!
fi
echo "$z"
done
2 4 6 8 10
Beispiel mit den Aufruf-Argumenten eines Skriptes
echo '#!/usr/bin/env bash
echo das script $0 wurde mit $# Argumenten aufgerufen.
for i in $@ # Loop over all arguments
do
echo $i
done
' > example_script.sh
./example_script.sh # Loop over all arguments
das script ./example_script.sh wurde mit 0 Argumenten aufgerufen.
# erinnerung (( ... )) für arithmetiche Ausdrücke
for ((c=1; c<=5; c++)); do
echo "Welcome $c times"
done
Welcome 1 times Welcome 2 times Welcome 3 times Welcome 4 times Welcome 5 times
Beachten Sie: hier wurde das do
in dieselbe Zeile des for
gesetzt. Dafür muss
ein Semikolon als Trenner verwendet werden.
Man kann auch alles in einer Zeile schreiben, wie hier:
for c in {1..6} ; do echo "Welcome $c times"; done
Welcome 1 times Welcome 2 times Welcome 3 times Welcome 4 times Welcome 5 times Welcome 6 times
# Erinnerung: Brace Expansion
# Range
echo {1..6}
# geht auch rückwärts mit anderer Schrittweite
echo {10..0..2}
1 2 3 4 5 6 10 8 6 4 2 0
Hinweis: Mit select
für einen Loop lässt sich ein einfaches Eingabe-Menü realisieren, siehe
https://ryanstutorials.net/bash-scripting-tutorial/bash-loops.php#select
Mittels bash Skripten kann man auch einfach Dateien in Verzeichnissen für die weitere Verarbeitung iterieren.
for file in ./*.txt
do
echo $file
done
./countries.txt ./f2.txt ./f.txt ./mylog.txt ./t.txt
Beachte Sie, dass das Globbing ./*.txt
von der Shell aufgelöst wird, sodass
effektiv dort ein Liste von Strings steht. So ähnlich, wie hier gezeigt:
for file in a.txt b.txt c.txt
do
echo $file
done
a.txt b.txt c.txt
Beispiel:
cat >case-example.sh << EOF
#!/usr/bin/env bash
country=\$1
case \$country in
Marokko | Somalia)
echo "\$country liegt in Afrika."
;;
Brasilien | Peru)
echo "\$country liegt in Südamerika."
;;
*)
echo "\$country kenne ich nicht."
;;
esac
EOF
chmod u+x case-example.sh
./case-example.sh "Brasilien"
./case-example.sh "Kuba"
Brasilien liegt in Südamerika Kuba kenne ich nicht.
Hilfreich ist es für Skripte den Strict-Mode einzuschalten. Dazu beginnt man ein bash-Skript mit folgenden Zeilen:
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
Dies vermeidet subtile Fehler zur Laufzeit. Lesen Sie hierzu: unofficial-bash-strict-mode
Das Setzen des IFS
erfolgt hier mit ANSI-C-Quoting
Es gibt Checker-Werkzeuge, wie shellcheck, die Ihnen helfen Fehler zu finden.
Wenn bash-Skripte beendet werden, wird das pseudo-Signal EXIT
gesendet. Dieses kann mit Hilfe des Kommandos trap
abgefangen werden und z.B. eine Funktion aufgerufen werden, die Resourcen freigibt, z.B. im Skript erzeugte temporäre Dateien löscht oder Locks löst.
Mehr hierzu siehe http://redsymbol.net/articles/bash-exit-traps/
Schreiben Sie ein Skript, das alle MP3 Dateien in einem Ordner mit einer Zahl am Anfang, so umbenennt, dass die Zahl immer 3 Stellen hat. Hintergrund: Ein MP3-Player spielt die Dateien meist nach String-Sortierung ab.
1_Einleitung.mp3
die Datei 001_Einleitung.mp3
14_Das_Erwachen.mp3
die Datei 014_Das_Erwachen.mp3
Erzeugen Sie zum Testen Ihres Skriptes mit touch
und brace expansion hundert Dateien, mit dem passenden Muster.
Folgende Fragen sollten Sie ohne Nachschlagen beantworten können:
export
beim Setzen von Variablen?.bashrc
bzw. .bash_profile
? Was bedeutet der Punkt am Anfang des Dateinamens?Die spezielle Skript-Syntax können Sie nachschlagen. Diese brauchen Sie nicht auswendiglernen. Entwickeln Sie stattdessen einige Beispielskripte, z.B. um Aufgaben zu automatisieren.