====== Snippets: Shell ======
[[:snippets:start|← Zurück zu :snippets:start]]
==== Kompatibilität ====
Sofern nicht anders gekennzeichnet:
^ Betriebssystem ^ Shell ^
| macOS & GNU/Linux | ''bash'' & ''zsh'' |
===== tmux: Shell-Sitzungen im Hintergrund =====
Um eine Shell-Sitzung zu trennen und im Hintergrund weiter laufen zu lassen kann der Terminal-Multiplexer [[https://github.com/tmux/tmux/wiki|tmux]] verwendet werden.
Im Gegensatz zu ''disown'' kann man sich später wieder mit der Sitzung verbinden und dort weiter arbeiten wo man aufgehört hat — egal ob am prompt oder in einer Anwendung.
==== Sitzung starten ====
# Eine neue tmux-Session starten
tmux
# Eine neue Sitzung mit vorgegebenem Namen starten
tmux new -s my_session
==== Sitzung trennen/tmux Steuerung ====
# Von der aktuellen Sitzung trennen
tmux detach
Tastenkombinationen für //tmux// sind zweiteilig; nach dem drücken von ''+'' folgt der eigentliche Befehl:
# Von der aktuellen Sitzung trennen
+,
# Alle Tastenkombinationen anzeigen
+, >
==== Laufende Sitzungen anzeigen ====
tmux list-session
tmux ls
0: 1 windows (created Mon Jan 19 18:57:53 2026)
yt-dlp: 1 windows (created Mon Jan 19 18:24:51 2026)
==== Mit Sitzung verbinden ====
Mit zuletzt aktiver Sitzung verbinden:
tmux attach
Mit einer bestimmten Sitzung verbinden:
tmux attach -t yt-dlp
===== OpenSSH =====
* Neue Private-Keys als Typ ''ed25519'' generieren (Elliptic-Curve-Algorithmus, deshalb trotz kurzer Keys sicherer als ''rsa'' mit maximaler Länge)
* Als Kommentar idealerweise ''user@hostname'' setzen, da dieser an den Public-Key angehängt wird und so ein Key auf einem Remote-System einfach einem Client zugeordnet werden kann.
* [[https://man.archlinux.org/man/ssh-keygen.1|ssh-keygen(1) — ArchLinux manual page]]
Für moderne Systeme mit OpenSSH kann mit folgendem Befehl ein sicherer Key erzeugt werden. Außer bei Embedded-Distributionen (OpenWRT verwendet z.B. einen minimalen SSH-Server mit weniger Features) hatte ich bisher keine Probleme mit dem folgenden Befehl:
# ed25519-Key mit 100 statt 16 KDF-Runden (deutlich sicherer); Kommentar mit Brnutzer- und Hostname; leerem Passwort und ohne Nachfrage nach einem Dateinamen
ssh-keygen \
-t ed25519 \
-a 100 \
-C "$USER@$(hostname -f)" \
-N "" \
-f ~/.ssh/id_ed25519
===== journald/journalctl =====
* System-Log unter systemd-basierten GNU/Linux-Distributionen wie z.B. Debian oder ArchLinux
* [[https://wiki.archlinux.org/title/Systemd/Journal|systemd/Journal - ArchWiki]]
* [[https://man.archlinux.org/man/journalctl.1|journalctl(1) — ArchLinux manual page]]
> Hinweis: Das systemweite Journal kann nur von ''root'' und Mitgliedern der Gruppe ''systemd-journal'' angezeigt werden.
==== Systemlog seit Mitternacht ====
Folgt dem Journal und zeigt neue Meldungen umgehend an. Es wird nicht automatisch ein Pager (z.B. ''less'') gestartet, stattdessen wird über den Puffer des Terminal-Emulators zurück gescrollt.
journalctl \
--output=short-iso \
--no-hostname \
--since="$(date +%Y-%m-%d) 00:00:00" \
--no-pager \
--follow
==== Log seit Mitternacht für eine einzelne Unit ====
Wie das vorherige Beispiel, aber auf eine einzelne systemd-Unit beschränken, statt das gesamte System-Log anzuzeigen.
* Die dazu gekommene Option ''--service='' akzeptiert jegliche Unit-Typen
* Das Standard-Suffix ''.service'' muss dabei zwar nicht explizit genannt werden, ich persönlich bevorzuge aber den ganzen Unit-Namen, deshalb wird es hier genannt.
journalctl \
--output=short-iso \
--no-hostname \
--since="$(date +%Y-%m-%d) 00:00:00" \
--no-pager \
--follow \
--unit=apache2.service
==== Unit-Log der aktuellen Stunde ====
Zeigt das Log einer Unit seit dem Beginn der aktuellen Stunde an. Gegenüber dem vorherigen Beispiel wurde nur die Option ''--since'' abgeändert, sodass die Stunde von ''date'' gesetzt wird, statt ''00''.
* In der Praxis ist oft nicht mehr als die letzten 20-30 Minuten von Relevanz
* Durch Weglassen der Option ''--unit'' kann stattdessen das gesamte System-Log angezeigt werden.
journalctl \
--output=short-iso \
--no-hostname \
--since="$(date +%Y-%m-%d\ %H):00:00" \
--no-pager \
--follow \
--unit=NetworkManager.service
===== Textverarbeitung =====
**TODO: Einführungstext**
==== Einrückung ab der zweiten Zeile ====
Mit dem folgenden ''sed''-Befehl lässt sich jede Zeile eines Textes leicht einrücken:
sed "s/^/ /" input.txt
Aber was ist, wenn die Einrückung erst ab der zweiten Zeile erscheinen soll?
Im folgenden Beispiel soll eine mehrzeilige Meldung in eine Logdatei geschrieben werden: Vor der ersten Zeile soll der Zeitstempel stehen, jede weitere Zeile soll mit Leerzeichen um die Länge des Zeitstempel-Prefix eingerückt werden.
Dafür wird der Text von einem kleinen ''awk''-Skript verarbeitet, das abhängig von der Zeilennummer (in der Variable ''NR'' gespeichert) entweder die Original-Zeilr wieder ausgibt, oder eben mit einem Prefix.
# Die zu loggende Nachricht
msg="This is a message
with multiple lines."
# Zeitstempel und dessen Länge
timestamp=$(date --iso-8601=seconds)
timestamp_length=$(echo -n "$timestamp" | wc -c)
# Der Prefix für alle Zeilen außer der ersten.
# Enthält $timestamp_length mal ein Leerzeichen, plus vier weitere für die Klammern und Leerzeichen vor und nach dem Zeitstempel
line_prefix=$(yes " " | head -$((timestamp_length + 4)) | tr -d "\n")
# Der Zeitstempel ohne abschließenden Zeilenumbruch
printf "[${timestamp}] "
# Ausgabe der Nachricht $msg inklusive Prefix
# Das awk-Skript prüft eine if-Klausel ob die Zeilennummer NR mindestens 2 ist, und gibt dann die ursprüngliche Zeile $0 mit einem Prefix aus, und falls nicht ohne den Prefix.
# Lesbare Variante mit Zeilenumbrüchen und Einrückungen:
echo "${msg}" | awk "{
if (NR >= 2) {
print \"${line_prefix}\"$0;
} else {
print $0;
}
}"
# Kompakte Variante in einer einzelnen Zeile:
echo "${msg}" | awk "{ if (NR >= 2) { print \"${line_prefix}\"$0; } else { print $0;} }"
===== Just for fun: Ein cowsay-Dalek für fortune-Cookies =====
> Hier sollte irgendwann eine kurze Einführung stehen …
{{ :snippets:fortune_cowsay_dalek.png?350|}}
Installation unter macOS:
# macOS/Homebrew: fortune, cowsay und zusätzliche "Kühe" installieren
brew install fortune cowsay cowsay-org/cowsay/cowsay-files
# Suchpfad für cowsay-Kühe
export COWPATH="/opt/homebrew/share/cowsay/cows:/opt/homebrew/share/cowsay-files/cows"
Installation unter Fedora (cowsay-files muss hier von Hand installiert werden):
# Fedora: fortune und cowsay via Paketverwaltung installieren
sudo dnf install fortune-mod cowsay
# cowsay-files aus dem Git-Repository von Hand in
# ~/.local installieren
git clone https://github.com/paulkaefer/cowsay-files
cd cowsay-files
make prefix=$HOME/.local install
Natürlich sollte ''$COWPATH'' in der Shell-Konfiguration hinterlegt werden (speichert den aktuell gesetzten Wert der Vatiable dauerhaft):
# $COWPATH in .zshenv
echo -e "\n\n# \$COWPATH - Search path for cowsay(1)\nexport COWPATH=\"${COWPATH}\"\n" >> ~/.zshenv
# $COWPATH in .bashrc speichern
echo -e "\n\n# \$COWPATH - Search path for cowsay(1)\nexport COWPATH=\"${COWPATH}\"\n" >> ~/.bashrc
Nachdem alles eingerichtet ist kann es losgehen.
Im folgenden Beispiel wird ein kurzer (''-s'') ''fortune(6)''-Glückskeks aus der Datenbank //literature// an ''cowsay(1)'' weiter gereicht und mit einer der zusätzlichen Kühe angezeigt (wobei der Text in der Sprechblase maximal 42 Zeichen breit sein darf):
fortune -s literature \
| cowsay -f dalek -W 42
==== bash: Zitat von einem QOTD-Server ====
Statt eines ''fortune(6)''-Cookies kann zumindest in der Bash auch mein //Qoute of the day//-Server abgefragt werden. Wobei dieser genau genommen auf (größtenteils deutschsprachige) fortune-Datenbanken zurück greift.
Oder du führst das [[https://hub.docker.com/r/malte70/bbs.qotd|Docker-Image (malte70/bbs.qotd)]] selbst aus.
**Kleine Warnung**: Das ist mein erstes selbst gebautes Image überhaupt; es erlaubt bisher kaum Anpassungen wie sonst üblich über Umgebungsvariablen.
cowsay -f dalek -W 42
> //Info-Box//
>
> Die Bash erlaubt eine Eingabe-/Ausgabeumleitung von bzw. zu einem Netzwerkdienst. Dabei wird auf eine nicht wirklich existierende Datei nach dem Muster ''/dev/tcp//'' zugegriffen.