#format rst :title: Shell als täglicher Begleiter :author: Dario Kamuf / Lars Kruse :css: https://wiki.hack-hro.de/talk_styles/assets/css/style.css :description: Hackspace Rostock e.V. :data-scale: 2 :data-transition-duration: 1 ---- :: ________ ________ / ______ \ / ______ \ / / \ \ / / \ \ / / \ " / \ \ | | \ / | | | | ____ _ " _ _ | | | | / ___|| |__ ___| | | | | | | \___ \| '_ \ / _ \ | | | | | | ___) | | | | __/ | | | | | | |____/|_| |_|\___|_|_| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | ---- Überblick ========= * Erscheinungsformen * Grundlegendes * Interaktive Details * Mehr Komplexität! ---- Wortfindung =========== :: shell: n. [orig. Multics techspeak, widely propagated via Unix] 1. **[techspeak] The command interpreter used to pass commands to an operating system; so called because it is the part of the operating system that interfaces with the outside world.** 2. More generally, any interface program that mediates access to a special resource or server for convenience, efficiency, or security reasons; for this meaning, the usage is usually a shell around whatever. This sort of program is also called a wrapper. 3. A skeleton program, created by hand or by another program (like, say, a parser generator), which provides the necessary incantations to set up some task and the control flow to drive it (the term driver is sometimes used synonymously). The user is meant to fill in whatever code is needed to get real work done. This usage is common in the AI and Microsoft Windows worlds, and confuses Unix hackers. Historical note: Apparently, the original Multics shell (sense 1) was so called because it was a shell (sense 3); it ran user programs not by starting up separate processes, but by dynamically linking the programs into its own code, calling them as subroutines, and then dynamically de-linking them on return. The VMS command interpreter still does something very like this. *Jargon File* ---- Themeneinschränkung =================== * textbasierte Unix-Shells * speziell: ash / bash / dash * interaktive Nutzung und Programmierung ---- Aufgabenbeschreibung ==================== * Prozesssteuerung * Datenflüsse lenken * Variablenraum und Kommando-Historie verwalten ---- Shell als Schnittstelle ======================= Aus Sicht der Shell: * Eingabe: Mensch schreibt Text (Kommandos) * Ergebnis: passend abgeleitete Programmaufrufe (``man execve``) Kernaufgabe: Interpretation menschlicher Eingaben und Steuerung der resultierenden Prozesse. ---- Eingabe --------------------- .. code:: shell-session foo@erker:~$ history | while read id command; do echo "${#command} $command"; done | sort -n | tail -1 | cut -f 2- -d " " ---- Eingabe ------- .. code:: shell-session foo@erker:~$ history \ > | while read id command; do echo "${#command} $command"; done \ > | sort -n \ > | tail -1 \ > | cut -f 2- -d " " ... ---- Ergebnis -------- .. code:: c #include int execve(const char *filename, char *const argv[], char *const envp[]); ---- Syntax: Variablen ================= Die Shell kennt nur zwei Datentypen: * Zeichenketten * Wahrheitswerte Es gibt lediglich boolsche Operatoren. ---- Variablen: Zahlen sind Zeichenketten ------------------------------------ .. code:: shell-session foo@erker:~$ x=5 foo@erker:~$ echo "$x" 5 foo@erker:~$ echo 4 + x 4 + x foo@erker:~$ echo $((x + 2)) 7 ---- Variablen: Abgrenzung von der Umgebung -------------------------------------- .. code:: shell-session foo@erker:~$ x=Welt foo@erker:~$ echo "Hallo $x" Hallo Welt foo@erker:~$ echo "Hallo ${x}en" Hallo Welten ---- Variablenoperation: Zeichen zählen ---------------------------------- .. code:: shell-session foo@erker:~$ echo "Die '$x' besteht aus ${#x} Zeichen" Die 'Welt' besteht aus 4 Zeichen ---- Variablenoperation: Präfix oder Suffix ersetzen ----------------------------------------------- .. code:: shell-session foo@erker:~$ x=Ausgabe.pdf foo@erker:~$ echo "Umwandlung: pdf2ps '$x' '${x%.pdf}.ps'" Umwandlung: pdf2ps 'Ausgabe.pdf' 'Ausgabe.ps' foo@erker:~$ x=/dev/null foo@erker:~$ echo "Relativ: ${x#/} und absolut: $x" Relativ: dev/null und absolut: /dev/null ---- Syntax: Flusskontrolle ====================== Die üblichen Verdächtigen sind verfügbar: * Verzweigungen (if, case) * Schleifen (for, while) ---- .. code:: shell-session foo@erker:~$ x=12 foo@erker:~$ if [ "$x" -lt 15 ]; then echo "kleiner"; else echo "zu gross"; fi kleiner ---- .. code:: shell-session foo@erker:~$ for a in foo bar baz; do echo "$a"; done foo bar baz ---- .. code:: shell-session foo@erker:~$ x=0; while [ "$x" -lt 3 ]; do echo "$x"; x=$((x+1)); done 0 1 2 ---- Syntax: Funktionen ================== .. code:: shell-session foo@erker:~$ get_uptime_seconds() { cut -f 1 -d . /proc/uptime; } foo@erker:~$ echo "Systemlaufzeit: $(get_uptime_seconds) Sekunden" Systemlaufzeit: 905923 Sekunden ---- Funktionen mit Parametern ------------------------- .. code:: shell-session foo@erker:~$ first_line() { head -1 "$1"; } foo@erker:~$ first_line /etc/passwd root:x:0:0:root:/root:/bin/bash ---- Auswertung von Programmabläufen =============================== Jeder Programmaufruf liefert drei Informationen zurück: * eine Zeichenkette (Standardausgabe, bzw. Fehlerausgabe) * eine Zahl (Exit-Code) * Erfolg / Fehler (ergibt sich aus ``Exit-Code == 0``) ---- Ausgabe eines Programms speichern --------------------------------- .. code:: shell-session foo@erker:~$ x=$(date) foo@erker:~$ echo "$x" Mi 8. Apr 04:49:08 CEST 2015 ---- Einfache Kommando-Expansion --------------------------- .. code:: shell-session foo@erker:~$ echo "Systemlaufzeit: $(cut -f 1 -d . /proc/uptime) Sekunden" Systemlaufzeit: 904953 Sekunden ---- Verschachtelte Kommando-Expansion --------------------------------- .. code:: shell-session foo@erker:~$ gnu_tage=$(( $(date --date="09/27/1983" +%s) / 3600 / 24)) foo@erker:~$ echo "Heute ist der Tag $gnu_tage des GNU-Projekts." Heute ist der Tag 5016 des GNU-Projekts. ---- Wahrheitswerte ============== * Wahrheitswerte existieren ausschließlich als Exitcodes von Programmen * es gibt keine Speichermöglichkeit * Hilfsmittel für Zeichenketten-, Zahlen- und Dateiprüfungen: ``test`` bzw. ``[`` * siehe ``man test`` bzw. ``man "["`` (synonym) ---- Exit-Code eines Programms prüfen -------------------------------- .. code:: shell-session foo@erker:~$ true; echo "$?" 0 foo@erker:~$ false; echo "$?" 1 ---- Zeichenketten auswerten ----------------------- .. code:: shell-session foo@erker:~$ x=Welt foo@erker:~$ [ "$x" = "Leute" ]; echo $? 1 foo@erker:~$ [ "$x" != "Leute" ]; echo $? 0 ---- Zahlen auswerten ---------------- .. code:: shell-session foo@erker:~$ x=12 foo@erker:~$ [ "$x" -lt 15 ]; echo $? 0 foo@erker:~$ [ "$x" -eq 15 ]; echo $? 1 ---- Dateiinformationen auswerten ---------------------------- .. code:: shell-session foo@erker:~$ [ -e /etc/passwd ]; echo $? 0 foo@erker:~$ [ -d /etc/passwd ]; echo $? 1 ---- Zusammenfassung: Logische Auswertung ------------------------------------ ``man test`` ---- Logische Verknüpfungen ---------------------- Mittels ``&&`` und ``||`` lassen sich im Sinne unvollständiger boolscher Auswertung Aktionen verknüpfen. ``if``-Konstruktionen erfüllen denselben Zweck - je nach Komplexität sind sie eventuell übersichtlicher. ---- .. code:: shell-session foo@erker:~$ [ -d /etc/apache2 ] && echo "Apache2 ist installiert" Apache2 ist installiert foo@erker:~$ if [ -d /etc/apache2 ]; then echo "Apache2 ist installiert"; fi Apache2 ist installiert ---- Selektor-Muster --------------- .. code:: shell-session foo@erker:~$ minimum() { [ "$1" -lt "$2" ] && echo "$1" || echo "$2"; } foo@erker:~$ minimum 12 8 8 foo@erker:~$ minimum 12 214 12 ---- Interaktive Funktionen ====================== Die interaktive ``bash`` unterstützt vielerlei Funktionen für effizientes Tun. Die einfachere ``dash`` dagegen enthält nicht diese Fähigkeiten. Die interaktiven Funktionen werden von der ``readline``-Bibliothek bereitgestellt. ---- Eingabemodi ----------- * Standard: Emacs-Modus * alternativ verwendbar: VI-Modus (``set -o vi``) * anschließend verhält sich die Kommandozeile wie VIM ---- Autovervollständigung --------------------- * Tab-Taste: ein oder mehrmals betätigen, um möglichst weit zu vervollständigen * typischerweise Vervollständigung basierend auf existierenden Dateinamen * Vervollständigung via ``bash-completion`` für viele Programme ---- Verwendung der History ---------------------- * ``STRG-R``: inkrementelle Suche * ``ESC`` gefolgt von ``.`` (Punkt): der letzte Parameter der vorherigen Kommandozeile ---- Interpretationsablauf ===================== 1. Quoting anwenden 2. Aufteilung in einzelne Kommandos: ``& && ( ) ; ;; | || \n`` 3. Word Expansion (Variablen, Kommandos, Berechnungen) 4. Field Splitting 5. Pathname Expansion 6. Quote Removal 7. Ausführung! ---- :: ___ _ _ / _ \ _ _ ___ | |_(_)_ __ __ _ | | | | | | |/ _ \| __| | '_ \ / _` | | |_| | |_| | (_) | |_| | | | | (_| | \__\_\\__,_|\___/ \__|_|_| |_|\__, | |___/ ---- .. code:: shell-session foo@erker:~$ x="echo bar; ls"; echo $x ---- .. code:: shell-session foo@erker:~$ x="echo bar; ls"; echo $x echo bar; ls ---- .. code:: shell-session foo@erker:~$ x="echo bar; ls"; echo $x echo bar; ls foo@erker:~$ x="echo bar; ls"; $x ---- .. code:: shell-session foo@erker:~$ x="echo bar; ls"; echo $x echo bar; ls foo@erker:~$ x="echo bar; ls"; $x bar; ls ---- .. code:: shell-session foo@erker:~$ x="echo bar; ls"; echo $x echo bar; ls foo@erker:~$ x="echo bar; ls"; $x bar; ls foo@erker:~$ x="echo bar; ls"; "$x" ---- .. code:: shell-session foo@erker:~$ x="echo bar; ls"; echo $x echo bar; ls foo@erker:~$ x="echo bar; ls"; $x bar; ls foo@erker:~$ x="echo bar; ls"; "$x" bash: echo bar; ls: Kommando nicht gefunden. ---- .. code:: shell-session foo@erker:~$ x="echo bar; ls"; echo $x echo bar; ls foo@erker:~$ x="echo bar; ls"; $x bar; ls foo@erker:~$ x="echo bar; ls"; "$x" bash: echo bar; ls: Kommando nicht gefunden. foo@erker:~$ x="echo bar; ls"; eval "$x" ---- .. code:: shell-session foo@erker:~$ x="echo bar; ls"; echo $x echo bar; ls foo@erker:~$ x="echo bar; ls"; $x bar; ls foo@erker:~$ x="echo bar; ls"; "$x" bash: echo bar; ls: Kommando nicht gefunden. foo@erker:~$ x="echo bar; ls"; eval "$x" bar aufnahmen conditioning.png debugger.png ---- :: _____ _ _ _ ____ _ _ _ _ _ | ___(_) ___| | __| | / ___| _ __ | (_) |_| |_(_)_ __ __ _ | |_ | |/ _ \ |/ _` | \___ \| '_ \| | | __| __| | '_ \ / _` | | _| | | __/ | (_| | ___) | |_) | | | |_| |_| | | | | (_| | |_| |_|\___|_|\__,_| |____/| .__/|_|_|\__|\__|_|_| |_|\__, | |_| |___/ _ __ __ _ _ __ __ _ ___| |__ \ \ / /__ _ __ __| | | '_ \ / _` |/ __| '_ \ \ \ /\ / / _ \| '__/ _` | | | | | (_| | (__| | | | \ V V / (_) | | | (_| | |_| |_|\__,_|\___|_| |_| \_/\_/ \___/|_| \__,_| _____ _ | ____|_ ___ __ __ _ _ __ ___(_) ___ _ __ | _| \ \/ / '_ \ / _` | '_ \/ __| |/ _ \| '_ \ | |___ > <| |_) | (_| | | | \__ \ | (_) | | | | |_____/_/\_\ .__/ \__,_|_| |_|___/_|\___/|_| |_| |_| * Variablen können in mehrere Felder separiert werden * besonders beliebt und überraschend bei Dateinamen mit Leerzeichen * Quoting hilft! ---- .. code:: shell-session foo@erker:~$ x="-2 /etc/passwd"; tail $x ---- .. code:: shell-session foo@erker:~$ x="-2 /etc/passwd"; tail $x liquidsoap:x:144:159::/usr/share/liquidsoap:/bin/false foo:x:1008:1008:,,,:/home/foo:/bin/bash ---- .. code:: shell-session foo@erker:~$ x="-2 /etc/passwd"; tail $x liquidsoap:x:144:159::/usr/share/liquidsoap:/bin/false foo:x:1008:1008:,,,:/home/foo:/bin/bash foo@erker:~$ x="-2 /etc/passwd"; tail "$x" ---- .. code:: shell-session foo@erker:~$ x="-2 /etc/passwd"; tail $x liquidsoap:x:144:159::/usr/share/liquidsoap:/bin/false foo:x:1008:1008:,,,:/home/foo:/bin/bash foo@erker:~$ x="-2 /etc/passwd"; tail "$x" tail: Option in ungültigen Kontext benutzt – 2 ---- :: __ __ _ _____ _ \ \ / /__ _ __ __| | | ____|_ ___ __ __ _ _ __ ___(_) ___ _ __ _ \ \ /\ / / _ \| '__/ _` | | _| \ \/ / '_ \ / _` | '_ \/ __| |/ _ \| '_ \(_) \ V V / (_) | | | (_| | | |___ > <| |_) | (_| | | | \__ \ | (_) | | | |_ \_/\_/ \___/|_| \__,_| |_____/_/\_\ .__/ \__,_|_| |_|___/_|\___/|_| |_(_) |_| ____ _ | __ ) ___ _ __ ___ ___| |__ _ __ _ _ _ __ __ _ ___ _ __ | _ \ / _ \ '__/ _ \/ __| '_ \| '_ \| | | | '_ \ / _` |/ _ \ '_ \ | |_) | __/ | | __/ (__| | | | | | | |_| | | | | (_| | __/ | | | |____/ \___|_| \___|\___|_| |_|_| |_|\__,_|_| |_|\__, |\___|_| |_| |___/ ---- .. code:: shell-session foo@erker:~$ x=4 ---- .. code:: shell-session foo@erker:~$ x=4 foo@erker:~$ echo $((x * 3)) ---- .. code:: shell-session foo@erker:~$ x=4 foo@erker:~$ echo $((x * 3)) 12 ---- .. code:: shell-session foo@erker:~$ x=4 foo@erker:~$ echo $((x * 3)) 12 foo@erker:~$ x=8 echo $((x * 3)) ---- .. code:: shell-session foo@erker:~$ x=4 foo@erker:~$ echo $((x * 3)) 12 foo@erker:~$ x=8 echo $((x * 3)) 12 ---- .. code:: shell-session foo@erker:~$ x=4 foo@erker:~$ echo $((x * 3)) 12 foo@erker:~$ x=8 echo $((x * 3)) 12 foo@erker:~$ x=8; echo $((x * 3)) ---- .. code:: shell-session foo@erker:~$ x=4 foo@erker:~$ echo $((x * 3)) 12 foo@erker:~$ x=8 echo $((x * 3)) 12 foo@erker:~$ x=8; echo $((x * 3)) 24 ---- :: ____ _ _ _ _ / ___| ___ __ _ _ _ ___ _ __ | |_(_) ___| | | ___ \___ \ / _ \/ _` | | | |/ _ \ '_ \| __| |/ _ \ | |/ _ \ ___) | __/ (_| | |_| | __/ | | | |_| | __/ | | __/ |____/ \___|\__, |\__,_|\___|_| |_|\__|_|\___|_|_|\___| |_| _____ _ | ____|_ ___ __ __ _ _ __ ___(_) ___ _ __ | _| \ \/ / '_ \ / _` | '_ \/ __| |/ _ \| '_ \ | |___ > <| |_) | (_| | | | \__ \ | (_) | | | | |_____/_/\_\ .__/ \__,_|_| |_|___/_|\___/|_| |_| |_| Ein Kommando wird immer erst zum Zeitpunkt seiner Ausführung expandiert. ---- .. code:: shell-session foo@erker:~$ x=$(date +%s); sleep 3; echo $(( $(date +%s) - x)) ---- .. code:: shell-session foo@erker:~$ x=$(date +%s); sleep 3; echo $(( $(date +%s) - x)) 3 ---- Hinweise zum Nachschlagen ========================= * kurz und knapp: ``man dash`` * übertrieben ausführlich: ``man bash``