Filtern in Bash

Manchmal will man aus einer Variable oder einem Text alle Zahlen herausziehen oder verstecken. Das geht natürlich mit sed ganz gut, aber noch einfacher in der Bash selbst.

% VAR=1a2b3c4d
% echo "${VAR//[!0-9]/}"
1234
% echo "${VAR//[0-9]/}"
abcd

Man kann auch nur Buchstaben filtern, damit man Sonderzeichen mit herausfiltert. Oder oder…

% VAR="1a,2b.3c;4d"
% echo "${VAR//[!0-9]/}"
1234
% echo "${VAR//[!a-z]/}"
abcd
% echo "${VAR//[!a-z 0-9]/}"
1a2b3c4d
% echo "${VAR//[a-z 0-9]/}"
,.;
Filtern in Bash

Mehrere Variablen in bash gleichzeitig setzen

Wenn ich mit den Output von einem Befehl mehrere Variablen setzen will, kann ich dafür read und die <<< Dreieckklammern nutzen. So gehts:

% EINS=2
% ZWEI=1
% read EINS ZWEI <<< $({ echo 1; echo 2 ; })
% echo $EINS $ZWEI
1 2

Wenn man auf Google oder Bing nach <<< sucht, findet man nichts. Also, entweder bin ich der erste (!!!11!1!!elf!!11), der darüber schreibt, oder wahrscheinlicher, nach diesem String darf man aus technischen Gründen einfach nicht suchen.

Mehrere Variablen in bash gleichzeitig setzen

HTML emails mit mail.mailutils

Das Programm mail.mailutils (meist einfach mail) ist auf vielen Linux-Distributionen installiert. Man kann damit über die Shell Emails verschicken, was sehr praktisch ist für scripte und cron-jobs, die den Admin per Mail informieren sollen.

Manchmal will man auch mal ein wenig mit HTML spielen und sei es auch nur, um um den Text ein <pre> zu setzen, damit er mit einer Monospace-Schrift angezeigt wird und ASCII-Art und Tabellen ordentlich dargestellt werden.

So schickt man HTML:

# generiere random boundary
BOUNDARY=$(date | md5sum | head -c8)
echo "
--_$BOUNDARY_
Content-Type: text/html; charset=\"us-ascii\"
Content-Transfer-Encoding: quoted-printable

<html><pre>
Meine Tabelle

+-------+----------+
| id    | analysis |
+-------+----------+
| 36190 |      219 |
| 36192 |      219 |
| 36273 |      219 |
| 36275 |       -1 |
| 36276 |       -1 |
| 36289 |      219 |
| 36293 |      122 |
| 36371 |      219 |
| 36388 |      219 |
| 36393 |      219 |
| 36394 |       -1 |
| 36395 |       -1 |
| 36708 |      122 |
+-------+----------+
</pre></html>
--_$BOUNDARY_--

" | mail.mailutils -s "Mein Subject" \
-a Content-Type:"multipart/alternative; boundary="\"_$BOUNDARY_\"" \
-a From:mein@absender.tld mein@empfaenger.tld

Diese Tabelle sieht jetzt sauber aus, wenn sie den Empfänger erreicht. Die Leerzeilen vor und nach dem Boundary und/oder Headern sind wichtig!

Wenn ich ein Bild in das HTML einbetten will, wird es komplizierter. Das mail-Tool kann zwar mit -A Anhänge verschicken, aber die kann ich dann nicht im HTML nutzen, da ich die dazugehörige ID nicht kenne.
Also muss ich das händisch machen, damit ich alle Parameter kontrollieren kann. Da Email ein 7-Bit Medium ist, muss ich aus dem Binär-Bild ASCII machen und dann noch dem Mailprogramm erklären, wie es das ASCII wieder in eine Binär-Bilddatei zurückverwandelt. Ich benutze hier base64 dafür, da das standardmässig installiert ist und so ziemlich jedes Mailprogramm das versteht. Ausserdem muss ich dem Bild einen Namen geben, denn der Dateiname geht verloren beim encoden in base64, und dazu eine ID, mit der ich darauf verweisen kann im HTML-Code der Mail. So gehts:

# generiere random boundary und Image ID
BOUNDARY=$(date | md5sum | head -c8)
IMAGEID=$(date | md5sum | head -c10 | rev)
# meine Bilddatei
IMAGEFILE=graph.png
( echo "
--_$BOUNDARY_
Content-Type: text/html; charset=\"us-ascii\"
Content-Transfer-Encoding: quoted-printable

<html>
Mein Graph als Bild:<br>
<img contenttype=3D\"image/png\" src=3D\"cid:$IMAGEID\"><br>
Sieht gut aus!

</pre></html>

--_$BOUNDARY_
Content-Type: image/png;name=\"$IMAGEFILE\"
Content-Disposition: attachment;filename=\"$IMAGEFILE\"
Content-ID: <$IMAGEID>
Content-Transfer-Encoding: base64 
"
base64 $IMAGEFILE 
echo "
--_$BOUNDARY_--" ) |  mail.mailutils -s "Mein Subject" \
-a Content-Type:"multipart/related; boundary="\"_$BOUNDARY_\""; type="\"multipart/alternative\""" \
-a MIME-Version:"1.0" \
-a From:mein@absender.tld mein@empfaenger.tld

Es gibt ausser mail.mailutils noch viele andere Commandline-Mailer wie etwa mutt oder mailx. Mit Hilfe der Beispiele oben kann man auch diese Mailer dazu bringen, HTML zu verschicken. Einfach mal gucken, welche Tools auf dem System installiert sind. Auch OS X kann das.

HTML emails mit mail.mailutils

find nur eins

Ich musste in einem Ordner alle Dateien durchsuchen nach einem String, interessierte mich aber nur dafür, ob es überhaupt eine Datei gibt, die diesen String hat. Leider waren es zu viele Dateien für ein fgrep *, also musste ich mit find und exec arbeiten. Aber da find eine Schleife durch alle gefundenen Dateien macht, muss man es irgendwie nach dem ersten Treffer abwürgen. So gehts:

find . -type f -exec bash -c 'fgrep -lv <string> {} && kill $PPID ' \;
find nur eins

sudo-matisch

Mit sudo kann man mit den Rechten eines anderen Users Befehle ausführen. Üblicherweise ist das root und wird gebraucht, um ohne root zu sein, root-Rechte zu bekommen. Andere User gehen aber so auch: sudo -u username <command>

Welche Benutzer mit sudo andere Rechte erlangen dürfen, steht in der Datei /etc/sudoers, die man nur mit dem Befehl visudo bearbeiten sollte. Je nach OS und Distribution findet man hier andere Voreinstellungen. Auf meinem Mac habe ich mir etwa einen unpriviligierten User angelegt (ist sicherer), aber mir in der sudoers-Datei trotzdem alle Rechte zugestanden (ist komfortabler).

# User privilege specification
eliyah ALL=(ALL:ALL) ALL

Aber sudo lässt sich auch für andere Dinge nutzen, etwa Scripte. Man will nicht immer ein Script, das für nur einen Befehl root-Rechte benötigt, komplett als root laufen lassen. Etwa, weil es von einem anderen Prozess, der selbst keine root Rechte besitzt, getriggert wird. Ein Beispiel sind cgi’s für den Webserver. Ein anderes sind per ssh remote ausgeführte Kommandos.

Ausserdem will man in scripten oder remote kein Passwort eintippen müssen, wenn man per sudo Befehle ausführt, will aber in seinem System nicht gleich für alle Tür und Tor öffnen.

Der Trick ist, Befehle zu definieren, die ein User ohne Passwort ausführen darf. Das geht über die Cmnd_Alias Funktion. Hier im Beispiel eine Befehlsdefinition, der es einem User erlaubt, Passworte für andere User zu setzen und einer, der erlaubt, per tcpdump Traffic mitzulesen:

# Cmnd alias specification
Cmnd_Alias PASSWD = /usr/bin/passwd [A-z]*, !/usr/bin/passwd root
Cmnd_Alias TCPDUMP = /usr/sbin/tcpdump

Was man hier schön sehen kann ist, dass man nicht nur Programme, sondern auch Parameter bestimmen kann, die erlaubt sind. Mit dem Ausrufezeichen (!) kann man auch Dinge wieder ausschliessen, hier eben das Ändern des Passwortes für den User root.

Wenn der Befehlalias definiert ist, kann man bestimmten Usern diese Befehle erlauben, sogar ohne Eingabe des Passwortes.

# User privilege specification
eliyah ALL=(ALL:ALL) PASSWD
eliyah ALL=(ALL) NOPASSWD: TCPDUMP

Die verschiedenen ALLs erlauben, von wo aus man das darf. Wie im Detail, weiss ich nicht, interessiert mich auch nicht. Denn ich kann jetzt ein Script schreiben, das als User eliyah läuft und mit Hilfe von sudo ohne Passwortabfrage einen tcpdump machen kann.

sudo-matisch