#format rst

:title: Shell als täglicher Begleiter
:author: Lars Kruse
:css: https://wiki.hack-hro.de/talk_styles/assets/css/style2.css
:description: Hackspace Rostock e.V.
:data-scale: 0.7
:data-transition-duration: 1

----

::

	       ________           ________
	     /  ______  \       /  ______  \
	   /  /        \  \   /  /        \  \
	 /  /            \  "  /            \  \
	|  |               \ /               |  |
	|  |      ____  _   "      _ _       |  |
	|  |     / ___|| |__   ___| | |      |  |
	|  |     \___ \| '_ \ / _ \ | |      |  |
	 |  |     ___) | | | |  __/ | |     |  |
	  |  |   |____/|_| |_|\___|_|_|    |  |
	    |  |                         |  |
	      |  |                     |  |
		|  |                 |  |
		  |  |             |  |
		    |  |         |  |
		      |  |     |  |
			|  | |  |
			  | | |
			   | |
			    |

----

Überblick
=========

* Erscheinungsformen
* Programmaufrufe
* Variablen
* Interaktive Details
* Pipes!
* Optional: Anatomie des shell-Parsers

----

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

----

Der göttliche Kleber
====================

All die schönen Werkzeuge (z.B. GNU coreutils) wären auf sich allein gestellt, gäb es keine Shell, die folgendes tut:

* 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 <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 boolesche und mathmatische 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

----

Programmaufrufe: Eingabe
========================

Bei einem Programmaufruf gibt es drei Informationsflüsse:

* Parameter
* Standardeingabe
* Umgebungsvariablen

----

Programmaufrufe: Rückgabe
=========================

Jeder Programmaufruf liefert drei Informationen zurück:

* Standardausgabe (Zeichenkette)
* Fehlerausgabe (Zeichenkette)
* Exit-Code (Ganzzahl; Null -> Erfolg)

----

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.

----

Standardausgabe eines Programms speichern
-----------------------------------------

.. code:: shell-session

	foo@erker:~$ x=$(date)
	foo@erker:~$ echo "$x"
	Mi 8. Apr 04:49:08 CEST 2015

----

Fehlerausgabe eines Programms speichern
---------------------------------------

.. code:: shell-session

	foo@erker:~$ fehler=$(ls NOTHING 2>&1 >/dev/null)
	foo@erker:~$ echo "$fehler"
	ls: Zugriff auf NOTHING nicht möglich: Datei oder Verzeichnis nicht gefunden

----

Wahrheitswerte
==============

* Wahrheitswerte existieren ausschließlich als Exitcodes von Programmen
* 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

----

Exit-Code aus Sicht des Programms
---------------------------------

Das Programm ``/bin/true`` sieht etwa folgendermaßen aus:

.. code:: c

	int main() {
		return 0;
	}

Das Programm ``/bin/false`` dagegen könnte dies sein:

.. code:: c

	int main() {
		return 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

----

Introspektion
-------------

Was ist das?

.. code:: shell-session

	foo@erker:~$ type echo
	echo ist eine von der Shell mitgelieferte Funktion.

Wer macht das?

.. code:: shell-session

	foo@erker:~$ which echo
	/bin/echo


Und sonst?

.. code:: shell-session

	foo@erker:~$ set | wc -l
	7341

----

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.

META: im bash-Manual wird die Meta-Taste als ``M`` verwendet - die ist entweder ESC oder ALT.

----

Eingabemodi
-----------

* Standard: Emacs-Modus
* alternativ verwendbar: VI-Modus (``set -o vi``)

  * anschließend verhält sich die Kommandozeile ähnlich zu VIM

----

Schnelle Bewegung
-----------------

* M-b: ein Wort zurück
* M-f: ein Wort vorwärts
* STRG-a: Beginn der Zeile
* STRG-w: Wort löschen

----

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
----------------------

* Cursor-Tasten hoch/runter: Historie durchblättern
* ``history``: letzte Kommandozeilen anzeigen
* ``STRG-R``: inkrementelle Suche
* ``M-STRG-y``: erster Parameter der vorherigen Kommandozeile
* ``M-.`` (Punkt): der letzte Parameter der vorherigen Kommandozeile

----

Shell-Expansion verstehen I
---------------------------

.. code:: shell-session

	foo@erker:~$ echo $(date +%s)

*Word Expansion* auflösen: ESC STRG-e

.. code:: shell-session

	foo@erker:~$ echo echo 1428512497

----

Shell-Expansion verstehen II
----------------------------

.. code:: shell-session

	foo@erker:~$ ls

*Word Expansion* auflösen: ESC STRG-e

.. code:: shell-session

	foo@erker:~$ ls --color=auto

----

Komplexe Mehrzeiler komfortabel komponieren
-------------------------------------------

``STRG-x STRG-e``

* öffne die aktuelle Zeile in einem Editor
* nach Beenden des Editors wird die Zeile / das Skript ausgeführt

----

Verknüpfung von Datenströmen: Pipelines
=======================================

Das Kern-Feature der Shell: die Funktionalität verschiedenster Programme vereinen.

----

Was passiert?
-------------

.. code:: shell

	du -s * \
		| sort -n \
		| while read size item; do
			du -sh "$item"
		done

----

Was passiert?
-------------

.. code:: shell

	history \
		| while read id command; do echo "${#command} $command"; done \
		| sort -n \
		| tail -1 \
		| cut -f 2- -d " "

----

Was passiert?
-------------

.. code:: shell

	openvpn $(grep -v "^$" "$config_file" \
		| grep -v "^#" \
		| grep -v "^remote" \
		| sed 's/^/--/' \
		| tr '\n' ' ') --remote "$host"

----

Was passiert?
-------------

.. code:: shell

	ssh sprachrohr "tail -100 /var/log/asterisk/cdr-csv/Master.csv" \
		| cut -f 3 -d "," \
		| cut -f 2 -d '"' \
		| sort \
		| uniq -c \
		| sort -n

----

Was passiert?
-------------

	ssh sprachrohr "tail -100 /var/log/asterisk/cdr-csv/Master.csv" \
		| awk '
			BEGIN { FS=","; x=0; }
			{ x += $15; }
			END { print x; }'

----

Was passiert?
-------------

.. code:: shell

	find ~ -type f -mtime -10 -name "*.png" \
		| grep -v "^[0-9]\{8\}-[0-9]\{4\}_" \
		| while read fname; do
			prefix=$(date --reference "$fname" +%Y%m%d-%H%M%S)
			new_name="$(dirname "$fname")${prefix}_$(basename "${fname%.png}").png"
			mv "$fname" "$new_name"
		done

----

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``