Prof. Dr.-Ing. Oliver Radfelder
Informatik / Wirtschaftsinformatik
Hochschule Bremerhaven
Snippets
Hier sammle ich kleine Schnipsel von Programmcode oder Konfigurationselementen, von denen ich hoffe, dass sie irgendjemandem nutzen könnten.
Die kleinen Dinge
#!/bin/bash
#gestern zur gleichen zeit
date -d '1 day ago' +%FT%T
#vor einem Monat
date -d '1 month ago' +'%FT00:00:00'
#Monatsanfang
date +"%Y-%m-01"
#Zeit von CET in Z(Zulu = UTC+0) (Elli ...)
TZ=Z date +"%Y-%m-%dT%H:%M:00|%s" -d "22 Dec 2020 15:00 CET"
#Heute in Elli
today=$(LC_ALL=en_EN.utf8 date +"%d. %b %Y,")
#Gestern in Elli
yesterday=$(LC_ALL=en_EN.utf8 date --date "1 day ago" +"%d. %b %Y,")
#Vorgestern in 12 Wochen - Impfabstand für astrazeneca 
date -d "-2 day +12 weeks"

#aktuelle Zeit in Nanosekunden seit dem 1.1.1970
#womit sich gut Zeiten vergleichen lassen
date +%s%N

#in der bash ab Version 5 - deutlich kürzer, da kein Prozess
#gestartet wird, als Mikrosekunden
echo ${EPOCHREALTIME//./}
printf -v start "%.3f" $EPOCHREALTIME; start=${start//./}

# mit ps Prozesse auflisten
ps -e -o uname:25=,pid=,ppid=,etimes=,comm=,args=

# mit top Prozess beobachten
while true; do top -bn1 -p 5292|tail -n+8; sleep 1; done
qrencode und zbarimg

In den docker-Containern sind die Tools qrencode und zbarimg aus den Debian-Repos installiert (apt install -y qrencode zbar-tools).

qrencode -o- -s20 "daddeldu 12345" > qr.png
zbarimg -q --nodbus qr.png

Mit ncat lässt sich ein wirklich kleiner Dienst realisieren:

#!/bin/bash
# qrencoder.sh
read line
qrencode -o- -s10 "$line"
# starte mit ncat -k -l -e ./qrencoder.sh localhost 2001

Für einen Test gibt es ein weiteres kleines Skript namens qrrequest.sh:

#!/bin/bash
# qrrequest.sh
echo "daddeldu 123"
cat >&2
# starte mit ncat -e ./request.sh localhost 2001 2>&1 | zbarimg --nodbus -q -
atomar Verzeichnis tauschen

atomically replacing files and directories

Ab demnächst ein Teil von linux-utils: exch

Zeilen aus Konfigurationsdateien mit sed löschen

Wer ein paar kleine, schnelle sed-Ausdrücke kennt, baut sich schnell das passende Verwaltungstool für Konfigurationsdateien wie .ssh/config:

# entferne alle Zeilen von /pattern1/ bis einschliesslich /pattern2/
sed -i "/pattern1/,/pattern2/d" ~/.ssh/docker_config

# entferne alle Zeilen zwischen /pattern1/ und /pattern2/
sed -i "/pattern1/,/pattern2/{//!d}" ~/.ssh/docker_config

Wenn ich docker-Container baue, in die ich mit ssh springen will, dann hilft es, eine zweite config-Datei zu haben, die in der .ssh/config am Anfang mit Include ~/.ssh/docker_config inkludiert wird. Dann werden beim Aktivieren eines Containers die passenden Zeile dort angehängt und beim Deaktivieren wieder herausgenommen:

# aktivieren
echo "host ${containername} # auto
  hostname ${ip}
  user user
" >> ~/.ssh/docker_config
# deaktivieren
sed -i "/host ${containername} # auto$/,/^$/{/.*/d}" ~/.ssh/docker_config
  

Das und mehr unter https://www.grymoire.com/Unix/Sed.html.

Dateien mit zufälligen Daten
Erzeugen Sie 50000 Dateien mit jeweils 1000000 zufälligen, druckbaren Zeichen in Zeilen von maximal 60 Zeichen Breite.
#!/bin/bash
for i in {00001..50000}; do 
  tr -dc "[[:alnum:]]" </dev/urandom | 
    head -c1000000 |
    fold -w60 >/tmp/file$i 
done 
tr -dc ... löscht alles außer der angegebenen Zeichenklasse.
</dev/urandom leitet die Eingabe aus dem Pseudozufalls-Device um.
{00001..50000} produziert die Zahlen in dem Bereich mit führenden Nullen.
head -c... schneidet die ersten Character heraus.
fold -w60 bricht Zeilen nach maximal 60 Charactern um.
Hashwerte erzeugen
Erzeugen Sie für die 50000 Dateien Hashwerte und schreiben Sie sie in eine Datei.
#!/bin/bash
for i in {00001..50000}; do 
  md5sum /tmp/file$i
done > /tmp/hashes
Oder einfach direkt in der Shell ohne Schleife:
find /tmp/ -name 'file*' | xargs md5sum > /tmp/hashes
LaTeX Snippets einbinden
In der Hauptdatei doc.tex:
\documentclass{scrartcl}
\newcommand{\vorname}{NIX}
\begin{document}
  \input{datafile}
  Der Wert dieses Datensatzes: \vorname
\end{document}
Und in der Datei datafile.tex:
\renewcommand{\vorname}{Oliver}

Aber Achtung: Hacking with LaTeX

Mindestens: in dem Verzeichnis ~/.texmf die Datei texmf.cnf anlegen mit dem Inhalt:

openin_any = p
openout_any = p

Zusätzlich beim Aufruf -no-shell-exec mitgeben. Mit den Mechanismen lässt sich allerdings (siehe oben) das Lesen von beliebigen Dateien leider nicht verhindern.

Kleine Vim-Tips
Wenn Sie in Ihre .vimrc die Zeile
:iab  dts strftime("%Y-%m-%d")
eintragen, koennen Sie im Text durch Tippen von
dts
das aktuelle Datum einfuegen.
oradfelder / 2017-04-05
Regular Expressions in der bash

Das ist der empfohlene Weg: Den regulären Ausdruck einer Variablen zuweisen und dann in doppelten eckigen Klammern ohne doppelte Anführungszeichen darauf zugreifen:

RE="^list|depl$"
[[ "$SSH_ORIGINAL_COMMAND" =~ $RE ]] || exit 1
Copy And Paste

Im Netz finden sich mancherlei Seiten, auf denen Kommandos zu markieren sind, um sie direkt in die Kommandozeile zu kopieren. Kopieren Sie einmal die folgende Zeile und pasten Sie sie in das Eingabefeld darunter.

echo cool stuff for lazy$(echo and stupid) admins

oder gleich in die Kommandozeile. Dass an der Stelle $(echo and stupid) genauso gut etwas sehr gefährliches stehen könnte, versteht sich von selbst. Also Obacht...

psql in der shell
 
   psql -t -AF $'\t' -c "select * from employees;" | \
     while IFS=$'\t' read a b c; do echo "$a,$b,$c"; done
  
Read Character by Character in bash

In der Bash kann mit $'\0' das Nullbyte ausgegeben werden. Als Delimiter bei *read* sorgt es dafür, dass auch Newlines und Tabs einzeln eingelesen werden. Mit "$'$CH" wird der Inhalt der Variablen wieder ausgegeben - und kann so an printf %d übergeben werden, damit der hexcode ausgegeben wird.


    echo -e "moin\nmoin\tmoin"| \
      while read  -d $'\0' -N1  CH ; do 
        echo "[$CH]"
        # printf "%d\n" "'$CH"; # als Integer (%x hex)
      done
    
Der korrekte Weg, mit der Bash Dateibäume einzulesen

Das Nullbyte und der Slash sind die einzigen Zeichen, die in einem Unix-Dateinamen nicht enthalten sein können. Der Befehl *find* traversiert das Dateisystem von dem ersten Argument aus - -print0 trennt die Namen dann mit dem Nullbyte.


  while IFS= read -r -d $'\0' file; do
    echo "$file"
  done < <(find . -print0)
    
Indirekte Variablen, Arrays und assoziative Arrays

Es gibt verschiedene Wege, mit indirekten Variablen umzugehen:

mykey=k456
declare index_$mykey=value123 # bash specific
eval "index_$mykey=value123"  # posix compatible
read index_$mykey <<<"value123"
printf -v index_$mykey "value123"               

eval "echo \$$index_$mykey"
refname=index_k456
echo ${!refname}              # indirect access

Sie werden insbesondere zu der eval-basierten Lösung harsche Kommentare im Netz finden ('eval is evil') - aber denken Sie selber nach, welche Risiken damit wann verbunden sind...

Oft ist es sinnvoller - so die Bash ab Version 4 verfügbar ist - mit Arrays oder assoziativen Arrays (Hashmaps) zu arbeiten:

declare -a myarray  #index array
for i in {0..9}; do
  myarray[$i]=value$i
done
myarray+=(dynamicappend)
for i in "${myarray[@]}"; do echo "$i"; done
echo "size: ${#myarray[@]}"


declare -A mykeystomyvalues  # associative array
mykeystomyvalues[a456]="maybeacomplexvalue"
echo ${mykeystomyvalues[a456]}
for v in "${mykeystomyvalues[@]}"; do echo value: "$v"; done
for k in "${!mykeystomyvalues[@]}"; do 
  echo key: "$k" value: "${mykeystomyvalues[$k]}"; 
done

Dazu kommt, dass indizierte Arrays in der Bash sparse strukturiert sind (anders als beispielsweise in Java): Das heisst,

num=$((2**63-1))
mykeys[$num]=myvalue
echo ${mykeys[$num]}

geht, ohne dass 2^63-1 Array-Elemente alloziert werden. Solange es sich also bei den Schlüsseln um Zahlenwerte handelt, verhalten sich indizierte Arrays in der Bash wie Hashmaps.