Mehr Power im Terminal mit tmux – Teil 2

This entry is part 2 of 3 in the series tmux

Ausgehend von einer laufenden tmux Session schauen wir uns heute die grundlegende Bedienung an.

Innerhalb meiner tmux Session kann man beispielsweise mittels Ctrl-b c ein neues Fenster erstellen.

Man erkennt es im folgenden Bild daran, dass neben 0:bash- ein 1:bash* steht. Das Sternchen steht dabei für das gerade aktive Fenster.

Zwischen den verschiedenen Fenstern kann man über die folgenden Tastenkombinationen wechseln:

  • Ctrl-b 0 .. 9 um direkt zu einer Shell zu springen
  • Ctrl-b n für das nächste Shell-Fenster
  • Ctrl-b p für das letzte

Mittels Ctrl-b & schließt das aktuelle Shell-Fenster, tmux fragt sicherheitshalber aber noch einmal nach, ob man das Fenster wirklich schließen möchte.

Da viele identische „Bash“ Strings als Bezeichner schnell unübersichtlich sind, kann man mit Ctrl-b , das aktuelle Fenster umbenennen.

Mehr Power im Terminal mit tmux – Teil 1

This entry is part 1 of 3 in the series tmux

In den letzten Tagen habe ich mich ein wenig mit tmux beschäftigt, einem Terminal-Multiplexer für Linux und Unix, der aber auch im Windows-Subsystem für Linux funktioniert.

Was ist ein Terminal-Multiplexer? Die englische Wikipedia [1] schreibt dazu folgendes:

„tmux is a terminal multiplexer for Unix-like operating systems. It allows multiple terminal sessions to be accessed simultaneously in a single window. It is useful for running more than one command-line program at the same time. It can also be used to detach processes from their controlling terminals, allowing SSH sessions to remain active without being visible.“

Also:

  • man kann mehrere Terminal-Sessions gleichzeitig in einem Fenster offen haben, bricht die Verbindung ab
  • bricht die SSH-Verbindung ab, so bleibt die Session trotzdem erhalten

Die Installation war recht einfach, per apt install tmux holt man sich das Paket auf die Maschine. Um bei jedem Login tmux sofort zur Verfügung zu haben, habe ich gemäß [2] folgendes der ~/.profile hinzugefügt:

if command -v tmux &> /dev/null && [ -z "$TMUX" ]; then
    tmux attach -t default || tmux new -s default
fi

Startet man jetzt tmux, so erhält man den folgenden Bildschirm:

Im nächsten Teil schauen wir uns dann die grundlegende Bedienung an.

[1] https://en.wikipedia.org/wiki/Tmux
[2] https://www.tecmint.com/tips-for-tmux-terminal-sessions/

Ordner anlegen mit Python und Excel

Für das Github-Repository der Vorträge von Dante e.V. Vereinstagungen habe ich heute eine Menge Unterordner nebst entsprechenden README-Dateien anlegen dürfen. Da die manuelle Anlage zu lange gedauert hätte (und Verschwendung wertvoller Lebenszeit bedeutet hätte), habe ich ein kleines Python-Skript genutzt, das sich die Daten aus einer Excel-Datei geholt hat.

Die Excel-Datei Daten.xlsx hatte dabei den folgenden Aufbau:

Mit dem folgenden Skript habe ich pro Jahr jeweils zwei Unterordner angelegt und die README-Datei jeweils mit dem passenden Titel versorgt. Man hätte es noch schöner schreiben können, für einen Quick und Dirty Hack war es aber mehr als ausreichend.

import pandas as pd
import os
 
df = pd.read_excel('Daten.xlsx')
 
for index, row in df.iterrows():
    jahr = str(row['Year'])
 
    f1 = jahr + '-Frühling'
    f2 = jahr + '-Herbst'
    spring = row['Spring']
    autumn = row['Autumn']
 
    try:
        if not os.path.exists(f1):
            os.makedirs(f1)
    except OSError:
        print ('Fehler bei Verzeichnis ' +  directory)
 
    with open(f1+'/README.md', 'wt') as file:
        file.write('# Vorträge der Dante e.V. Frühjahrstagung '+jahr + '\n\n')
        file.write('Datum: \n')
        file.write('Veranstaltungsort: ' + spring +'\n')
 
    try:
        if not os.path.exists(f2):
            os.makedirs(f2)
    except OSError:
        print ('Fehler bei Verzeichnis ' +  directory)
 
 
    with open(f2+'/README.md','wt') as file:
        file.write('# Vorträge der Dante e.V. Herbsttagung '+jahr + '\n\n')
        file.write('Datum: \n')
        file.write('Veranstaltungsort: ' + autumn +'\n')

CUPS auf dem Raspi installieren, mit iOS drucken

Um vom iPhone und iPad drucken zu können, habe ich heute meinen Raspi (der in der Unterversorgung hängt und damit 24/7 läuft) mit CUPS versehen. Ich bin dabei der Anleitung von https://www.elektronik-kompendium.de/sites/raspberry-pi/2007081.htm gefolgt, es lief ohne Probleme.

Als etwas komplizierter hat sich die Duplizierung eines Druckers erwiesen. Ich habe sowohl die entsprechende ppd-Datei als auch den Eintrag in der printers.conf angepasst, jedoch sehen die iOS-Geräte nur den ersten Drucker.

Die Lösung dazu habe ich auf https://www.johnlose.de/2018/02/drucker-airprint-faehig-machen-auf-ubuntu-16-04-airprint-cups-ubuntu-xenial/ gefunden: der avahi-Daemon war noch nicht vollständig konfiguriert, der die Bonjour-Erkennung übernimmt.

Mit wget https://raw.githubusercontent.com/tjfontaine/airprint-generate/master/airprint-generate.py kann man jedoch ein Python-Skript herunterladen, das die Erzeugung der Drucker-Dateien übernimmt, die man dann nach /etc/avahi/services verschiebt. Nach dem Neustart des Dienstes mittels /etc/init.d/avahi-daemon restart findet das iPhone dann auch den Drucker vie Airprint, einmal für einseitig, einmal für zweiseitig.

Update: Aktuell druckt der Drucker nur über den einen nicht-AirPrint-Server, warum weiß ich noch nicht.

Ubuntu/Debian/Raspian Cheat-Sheet

Hier mein persönliches Cheat-Sheet für die Arbeit mit den verschiedenen Linux-Derivaten. Es wird im Laufe der Zeit erweitert.

Ubuntu

lsb_release -a
Ermittelt die Ubuntu Version
cat /proc/cpuinfo
CPU-Informationen ausgeben
cat /proc/meminfo
Memory-Informationen ausgeben
ip address show
IP-Adressen der Interfaces anzeigen

Netzwerkverbindung unter Windows reparieren

Wenn unter Windows die Netzwerkverbindung nicht mehr verfügbar ist, können die folgenden Befehle von Deskmodder helfen, die auf einer Admin-Konsole einzugeben sind:

  1. netsh winsock reset
  2. netsh int ip reset
  3. ipconfig /release
  4. ipconfig /renew
  5. ipconfig /flushdns

Python-Übungsaufgabe: MD5-Hashes

Hier eine Übungsaufgabe, die ich mir heute basierend auf https://www.programmieraufgaben.ch/aufgabe/baeckerei-b-rot/xyikp5d5 ausgedacht habe:

Eine Firma hat sich im Streit von ihrem alten Administrator getrennt und hat jetzt ein Problem. Das Passwort für das Lohnzahlungssystem hatte nur der Admin, man kann es nicht zurücksetzen und sollte es einmal falsch eingegeben werden, werden alle Daten der letzten zehn Jahre gelöscht.

Studentin Susanne, die gerade ein Praktikum bei der Firma macht, findet im Dateisystem des Rechners eine Datei, die einen MD5-Hash vom Passwort enthält. Zusätzlich ist bekannt, dass es sich beim Passwort um (verschiedene Fälle mit steigender Komplexität)

* eine vierstellige Zahl (Hash lautet \texttt{48c8c3963853fff20bd9e8bee9bd4c07})
* eine fünfstellige Zahl (Hash selbst ausdenken) oder
* eine sechsstellige Zahl oder
* einen String aus vier (oder fünf oder sechs) Ziffern und Kleinbuchstaben (super als Hausaufgabe!)

handelt. Schreiben Sie ein Programm, das das Passwort im Klartext auf Basis des MD5-Hash ausgibt.

Für die Bestimmung des MD5-Hash nutzen Sie eine passende Bibliothek, eine Implementierung des Algorithmus ist natürlich nicht notwendig.

Zwei Aufgabenblätter auf einer Seite mit LaTeX

Für meine Studierenden erstelle ich diverse Übungsblätter, damit das Thema „Python“ etwas anschaulicher wird. Dazu nutze ich ein angepasstes LaTeX-Template, mit dem ich die Seite dupliziere und in verkleinerter Form auf das DIN A4-Blatt bringe.

%!TEX TS-program = Arara
% arara: pdflatex: {shell: yes}
% arara: pdflatex: {shell: yes}
% arara: clean: { extensions: [ log, aux, nav, out, snm, vrb, toc ] }
 
\documentclass[a4paper,ngerman,12pt]{exam} 
 
\usepackage{babel}
\usepackage[a4paper,top=2.5cm,bottom=3cm,left=2.5cm,right=2cm]{geometry}
\usepackage[T1]{fontenc}
\usepackage{booktabs}
\usepackage{graphicx}
\usepackage{csquotes}
\usepackage{paralist}
\usepackage[math]{iwona}
\usepackage{textcomp}
\usepackage{listings}
\usepackage{xcolor}
 
\pointpoints{Punkt}{Punkte}
\bonuspointpoints{Bonuspunkt}{Bonuspunkte}
\renewcommand{\solutiontitle}{\noindent\textbf{Lösung:}\enspace}
 
\chqword{Frage}   
\chpgword{Seite} 
\chpword{Punkte}   
\chbpword{Bonus Punkte} 
\chsword{Erreicht}   
\chtword{Gesamt}
 
\pagestyle{headandfoot}
\runningheadrule
 
%%%%%%%
\definecolor{hellgelb}{rgb}{1,1,0.8}
\definecolor{lightgelb}{rgb}{1,1,0.8}
\definecolor{colKeys}{rgb}{0,0,1}
\definecolor{colIdentifier}{rgb}{0,0,0}
\definecolor{colComments}{rgb}{1,0,0}
\definecolor{colString}{rgb}{0,0.5,0}
 
\usepackage{listings}
\lstset{%
    float=hbp,%
    basicstyle=\ttfamily\footnotesize, %
    identifierstyle=\color{colIdentifier}, %
    keywordstyle=\color{colKeys}, %
    stringstyle=\color{colString}, %
    commentstyle=\color{colComments}, %
    columns=flexible, %
    tabsize=2, %
    frame=single, %
    upquote=true,%
    extendedchars=true, %
    showspaces=false, %
    showstringspaces=false, %
    numbers=left, %
    numberstyle=\tiny, %
    breaklines=true, %
    backgroundcolor=\color{hellgelb}, %
    breakautoindent=true, %
    captionpos=b%
}
 
%%%%%%%%%%%%
\lstset{literate=%
    {Ö}{{\"O}}1
    {Ä}{{\"A}}1
    {Ü}{{\"U}}1
    {ß}{{\ss}}1
    {ü}{{\"u}}1
    {ä}{{\"a}}1
    {ö}{{\"o}}1
    {~}{{\textasciitilde}}1
}
 
 
 
\usepackage{pgfpages}                                 % <— load the package
\usepackage{atbegshi}
 
\newcommand{\twoonone}{% 
  \pgfpagesuselayout{2 on 1}[a4paper,landscape,border shrink=5mm] % <— set options
  % duplicate the content at shipout time
  \AtBeginShipout{%
    \pgfpagesshipoutlogicalpage{1}\copy\AtBeginShipoutBox%
    \pgfpagesshipoutlogicalpage{2}\box\AtBeginShipoutBox%
    \pgfshipoutphysicalpage%
  }}
 
 
 
\firstpageheader{WS~2018/2019}{Skriptsprachenprogrammierung}{Dr.~Uwe Ziegenhagen}
\runningheader{WS~2018/2019}{Skriptsprachenprogrammierung}{Dr.~Uwe Ziegenhagen}
\firstpagefooter{\today}{}{\thepage\,/\,\numpages}
\runningfooter{\today}{Mitte unten}{\thepage\,/\,\numpages}
 
\setlength{\parindent}{0pt}
\setlength{\parskip}{6pt}
 
 
\twoonone % two pages on one
 
\begin{document}
 
\vspace*{0.5cm}
\begin{center}
\huge\bfseries Arbeitsblatt 02: Datentypen
\end{center}
\vspace*{0.5cm}
 
\section*{Aufgaben}
 
\begin{questions}
\question Welche Datentypen kennen Sie in Python?
 
\begin{itemize}
	\item 
	\item 
	\item 
	\item 
	\item 
\end{itemize}
 
\question Erstellen Sie verschiedene Variablen mit unterschiedlichen Datentypen! Führen Sie verschiedene Grundrechenarten mit den Datentypen durch.
 
\question Satz des Pythagoras: Berechnen Sie mittels Python die Hypotenuse eines Dreiecks, dessen Katheten 3 und 4 Zentimeter lang sind. Prüfen Sie die Datentypen der Variablen mittels \texttt{type()} Funktion! Warum gibt es Unterschiede in den Typen?
 
\question Richten Sie einen String beliebiger Länge bei der Ausgabe auf der Kommandozeile rechtsbündig aus! Hinweise: Gehen Sie von einer Zeilenbreite von 60 Zeichen aus. Benutzen Sie die \texttt{len()} Funktion, um die Länge der Zeichenkette zu bestimmen.
 
\question Nutzen Sie die \texttt{int()}, \texttt{float()} und \texttt{str()} Funktion, um verschiedene Datentypen umzuwandeln!
 
\question Hier kommt ein Listing
 
\begin{lstlisting}[language={Python}]
def create_bruch():
    zahlen = list(range(1,13))
    zaehler = random.choice(zahlen)
    zahlen.remove(zaehler)
    nenner = random.choice(zahlen)
    return 1234
\end{lstlisting}
 
\end{questions}
 
\end{document}

Adressen parsen mit Python und libpostal/pypostal

Beim letzten Treffen der PyCologne habe ich meinen Ansatz vorgestellt, um „Straße Hausnummer“ Kombinationen aus verschiedenen Ländern zu parsen. Jemand in der Gruppe gab mir dann den Hinweis auf libpostal und seine Python-Bindings.

Unter Windows habe ich es nicht zum Laufen bekommen, die Installationsroutine setzt noch diverse Compiler voraus. Unter Linux klappt die Installation, wenn man sich genau an die Anleitung unter https://github.com/openvenues/pypostal hält.

Dann kann man mittels parse_address() die Adressdaten verarbeiten.

(base) uwe@Micro:~$ python
Python 3.7.4 (default, Aug 13 2019, 20:35:49)
[GCC 7.3.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from postal.parser import parse_address
>>> parse_address("Meisenweg 1")
[('meisenweg', 'road'), ('1', 'house_number')]
>>> parse_address("10 Downing Street")
[('10', 'house_number'), ('downing street', 'road')]
>>> parse_address("10, Rue de Pommes Frites")
[('10', 'house_number'), ('rue de pommes frites', 'road')]
>>> parse_address("Am Geldspeicher 1, 12345 Entenhausen")
[('am geldspeicher', 'road'), ('1', 'house_number'), ('12345', 'postcode'), ('entenhausen', 'city')]
>>>

Vortragsfolien zum Thema „TDD mit Python“

Hier die Folien einer Präsentation, die ich auf den Python-Stammtischen in Düsseldorf und Köln gehalten habe.

PyDDF-TDD-UweZiegenhagen.pdf