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-transition-duration: | |
1 | |
data-scale: | 1.2 |
________ ________ / ______ \ / ______ \ / / \ \ / / \ \ / / \ " / \ \ | | \ / | | | | ____ _ " _ _ | | | | / ___|| |__ ___| | | | | | | \___ \| '_ \ / _ \ | | | | | | ___) | | | | __/ | | | | | | |____/|_| |_|\___|_|_| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
Überblick
- Erscheinungsformen
- Grundlegendes
- Interaktive Details
- Mehr Komplexität!
Wortfindung
shell: n. [orig. Multics techspeak, widely propagated via Unix]
- [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.
- 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.
- 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
foo@erker:~$ history | while read id command; do echo "${#command} $command"; done | sort -n | tail -1 | cut -f 2- -d " "
Eingabe
foo@erker:~$ history \ > | while read id command; do echo "${#command} $command"; done \ > | sort -n \ > | tail -1 \ > | cut -f 2- -d " " ...
Ergebnis
#include <unistd.h> 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
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
foo@erker:~$ x=Welt foo@erker:~$ echo "Hallo $x" Hallo Welt foo@erker:~$ echo "Hallo ${x}en" Hallo Welten
Variablenoperation: Zeichen zählen
foo@erker:~$ echo "Die '$x' besteht aus ${#x} Zeichen" Die 'Welt' besteht aus 4 Zeichen
Variablenoperation: Präfix oder Suffix ersetzen
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)
foo@erker:~$ x=12 foo@erker:~$ if [ "$x" -lt 15 ]; then echo "kleiner"; else echo "zu gross"; fi kleiner
foo@erker:~$ for a in foo bar baz; do echo "$a"; done foo bar baz
foo@erker:~$ x=0; while [ "$x" -lt 3 ]; do echo "$x"; x=$((x+1)); done 0 1 2
Syntax: Funktionen
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
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
foo@erker:~$ x=$(date) foo@erker:~$ echo "$x" Mi 8. Apr 04:49:08 CEST 2015
Einfache Kommando-Expansion
foo@erker:~$ echo "Systemlaufzeit: $(cut -f 1 -d . /proc/uptime) Sekunden" Systemlaufzeit: 904953 Sekunden
Verschachtelte Kommando-Expansion
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
foo@erker:~$ true; echo "$?" 0 foo@erker:~$ false; echo "$?" 1
Zeichenketten auswerten
foo@erker:~$ x=Welt foo@erker:~$ [ "$x" = "Leute" ]; echo $? 1 foo@erker:~$ [ "$x" != "Leute" ]; echo $? 0
Zahlen auswerten
foo@erker:~$ x=12 foo@erker:~$ [ "$x" -lt 15 ]; echo $? 0 foo@erker:~$ [ "$x" -eq 15 ]; echo $? 1
Dateiinformationen auswerten
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.
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
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
- Quoting anwenden
- Aufteilung in einzelne Kommandos: & && ( ) ; ;; | || \n
- Word Expansion (Variablen, Kommandos, Berechnungen)
- Field Splitting
- Pathname Expansion
- Quote Removal
- Ausführung!
___ _ _ / _ \ _ _ ___ | |_(_)_ __ __ _ | | | | | | |/ _ \| __| | '_ \ / _` | | |_| | |_| | (_) | |_| | | | | (_| | \__\_\\__,_|\___/ \__|_|_| |_|\__, | |___/
foo@erker:~$ x="echo bar; ls"; echo $x
foo@erker:~$ x="echo bar; ls"; echo $x echo bar; ls
foo@erker:~$ x="echo bar; ls"; echo $x echo bar; ls foo@erker:~$ x="echo bar; ls"; $x
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"; echo $x echo bar; ls foo@erker:~$ x="echo bar; ls"; $x bar; ls foo@erker:~$ x="echo bar; ls"; "$x"
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"; 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"
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!
foo@erker:~$ x="-2 /etc/passwd"; tail $x
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 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"
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 / (_) | | | (_| | | |___ > <| |_) | (_| | | | \__ \ | (_) | | | |_ \_/\_/ \___/|_| \__,_| |_____/_/\_\ .__/ \__,_|_| |_|___/_|\___/|_| |_(_) |_| ____ _ | __ ) ___ _ __ ___ ___| |__ _ __ _ _ _ __ __ _ ___ _ __ | _ \ / _ \ '__/ _ \/ __| '_ \| '_ \| | | | '_ \ / _` |/ _ \ '_ \ | |_) | __/ | | __/ (__| | | | | | | |_| | | | | (_| | __/ | | | |____/ \___|_| \___|\___|_| |_|_| |_|\__,_|_| |_|\__, |\___|_| |_| |___/
foo@erker:~$ x=4
foo@erker:~$ x=4 foo@erker:~$ echo $((x * 3))
foo@erker:~$ x=4 foo@erker:~$ echo $((x * 3)) 12
foo@erker:~$ x=4 foo@erker:~$ echo $((x * 3)) 12 foo@erker:~$ x=8 echo $((x * 3))
foo@erker:~$ x=4 foo@erker:~$ echo $((x * 3)) 12 foo@erker:~$ x=8 echo $((x * 3)) 12
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))
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.
foo@erker:~$ x=$(date +%s); sleep 3; echo $(( $(date +%s) - x))
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