Shuffle, Random und andere Funktionen Zufallsquellen in der Bash nutzen

Von Mirco Lang |

Anbieter zum Thema

Der Zufall ist ein mächtiges Werkzeug für Spiele, Tests oder Kryptografie. In der Bourne Again Shell oder kurz Bash ist Randomisierung gleich in mehreren Implementierungen verfügbar.

Drei der vier hier vorgestellten Zufallsfunktionen in der Bash funktionieren auch in anderen Shells.
Drei der vier hier vorgestellten Zufallsfunktionen in der Bash funktionieren auch in anderen Shells.
(Bild: Erik Stein (erik_stein) / Pixabay)

Beim Zufall sollte man lieber nichts dem Zufall überlassen – die falsche Zufallsquelle könnte den Zufall zu Fall bringen. So platt (und naheliegend) dieses Wortspielchen auch sein mag, die Aussage ist durchaus relevant: Für viele Aufgaben genügt es, einigermaßen zufällige Zahlen zu generieren oder Optionen zu wählen.

Spätestens bei Kryptografie- und Sicherheitsfunktionen ist die Qualität der Zufallsquelle jedoch essenziell. Meist geht es schlicht darum, eine Zahl aus einem bestimmten Bereich zu wählen, also letztlich eine Art Würfel-Funktion. Aber auch die zufällige Auswahl einer beliebigen Option steht immer wieder mal auf dem Plan, was wiederum dem blinden Ziehen einer Karte aus einem Deck entspricht – in der Praxis häufig als Shuffle-Funktion von Playlisten zu sehen.

Im Folgenden zeigen wir Ihnen vier Varianten, in der Bash mit dem Zufall zu arbeiten – drei davon funktionieren nicht ganz zufällig auch in anderen Shells. Zum Einsatz kommen die Bash-interne Funktion $RANDOM, das GNU-Core-Utilities-Werkzeug shuf, die Gerätedatei „/dev/urandom“ sowie die awk-Funktion rand.

shuf

Das nützliche kleine shuf gehört zu den GNU Core Utilities und steht für Shuffle, also das Mischen. Shuf eignet sich wunderbar, um mal eben Kleinigkeiten per Zufall zu erledigen, beispielsweise einen Münzwurf zu simulieren oder eine Option zu wählen. Dabei kann shuf quasi intern Zahlen bereitstellen oder Optionen einlesen.

Eigentlich ist shuf wirklich dazu gedacht, Eingabezeilen zu mischen und auszugeben. Hier mal ein Beispiel von shuf in der Grundform:

shuf -i 1-6

Hier werden alle Ziffern von 1 bis 6 genau einmal, in zufälliger Reihenfolge, ausgegeben – es wird also lediglich gemischt und keine zufällige Auswahl getroffen. Das lässt sich allerdings durch die Beschränkung auf ein einziges Ergebnis realisieren:

shuf -i 1-6 -n 1

Nun wird lediglich eine einzige Zeile ausgegeben, also letztlich ein Würfel-Wurf simuliert. Praktisch ist shuf zum Beispiel, um eine zufällige Mediendatei abzuspielen:

mediaplayer.exe $(ls medien/musik/ | shuf -n1)

Für kryptografische Zwecke sollten Sie shuf so allerdings nicht einsetzen, auch wenn die Input-Option (-i) Zahlen bis 10 Trillionen verträgt – denn die Standardquelle für Zufallsdaten, die shuf benötigt, ist ein interner Pseudozufallszahlengenerator mit geringer Entropie. Allerdings verträgt shuf auch manuelle Quellen:

shuf --random-source=/dev/random -i 1-10000 -n1

In diesem Fall wird die Gerätedatei „/dev/random“ als Zufallsquelle genutzt. Das geht aber auch etwas direkter.

Dev-random

Mit „/dev/random“ (und „/dev/urandom“) gibt es unter Linux eine etablierte und für Kryptografie geeignete Zufallsquelle, die unter anderem vom BSI (Bundesamt für Sicherheit in der Informationstechnik) ausgiebig evaluiert wurde. Der Zufall basiert hier auf einem Entropie-Pool, der über „Umgebungsgeräusche“ von zum Beispiel Gerätetreibern gespeist wird und gilt als äußerst randomisiert.

Was genau die Gerätedatei ausgibt, können Sie sich mit „cat /dev/random“ anschauen – aber Vorsicht, das Terminal-Fenster könnte dabei durchaus abstürzen oder den Abbruch verweigern. Sinnvoll wird der wirre Zeichensalat erst, wenn er in eine menschenlesbare Form gebracht wird, beispielsweise in Dezimalschreibweise:

od -A n -t d -N 1 /dev/random

Das Tool od (octal dump) kann eine gegebene Datei als eindeutige Repräsentation in diversen Formaten ausgeben, standardmäßig im Oktal-Format, hier jedoch im Dezimal-Format über die Option „-t d“. Da die hier angegebene Gerätedatei endlos ausgibt, wird od via „-N 1“ auf ein Byte der Ausgabe beschränkt.

Letztlich sorgt „-A n“ dafür, dass keine (n=none) Positionsangaben/Offsets mit ausgegeben werden. Das etwas unschöne od-Kommando sorgt also einfach nur dafür, dass die Zufallsausgabe von „/dev/random“ in Form einer Zahl aus dem Dezimalsystem erscheint.

$RANDOM

Jetzt wird es Bash-spezifisch: Die interne Funktion $RANDOM erzeugt Zufallszahlen zwischen 0 und 32767, also in einem relativ überschaubaren Bereich. Die Nutzung ist wunderbar simpel:

echo $RANDOM

In der Praxis geht es meist aber eher um Zahlen aus einem kleineren Bereich. Üblich ist die Verwendung mit Modulo in Form einer arithmetischen Operation – hier für Zahlen zwischen 1 und 10:

echo (( $RANDOM % 10 + 1 ))

Per Modulo stünden hier eigentlich die Zahlen 0-9 zur Verfügung, schließlich wäre die 10 selbst durch 10 Teilbar, daher das „+ 1“ – nur für den Fall, dass Schul-Mathe („Teilen mit Rest“) schon arg lange her ist.

Jetzt Newsletter abonnieren

Täglich die wichtigsten Infos zu Softwareentwicklung und DevOps

Mit Klick auf „Newsletter abonnieren“ erkläre ich mich mit der Verarbeitung und Nutzung meiner Daten gemäß Einwilligungserklärung (bitte aufklappen für Details) einverstanden und akzeptiere die Nutzungsbedingungen. Weitere Informationen finde ich in unserer Datenschutzerklärung.

Aufklappen für Details zu Ihrer Einwilligung

So hübsch einleuchtend die Kombination aus $RANDOM und Modulo ist, so fragwürdig ist sie im Hinblick auf mathematische Präzision: Nicht für alle Rest-Werte gibt es dieselbe Wahrscheinlichkeit. Bei „$(( $RANDOM % 10 ))“ ist die Chance, als Resultat 0-7 zu bekommen minimal höher, als bei 8 oder 9 zu landen. Das ist in der Theorie ganz interessant, vor allem aber heißt das, dass diese Variante für Sicherheitsfunktionen nicht geeignet ist.

Übrigens: Wenn $RANDOM mit „unset“ quasi „aufgelöst“ wird, bleibt der letzte Wert bestehen, sprich:

echo $RANDOM → 12345
echo $RANDOM → 20
nset $RANDOM
echo $RANDOM → 20

Hier ist also etwas Umsicht angebracht. Aber natürlich lässt sich $RANDOM auch anderweitig einsetzen, beispielsweise als Seed für den awk-internen Zufall.

Awk und (s)rand

Die Menge an Aufgaben, die awk bewältigen kann, ist immens. Und da awk sowieso ständig in Skripten vorkommt, bietet es sich auch für den Umgang mit dem Zufall an. Im einfachsten Fall übernimmt das die Funktion rand:

awk 'BEGIN{print rand()}'

rand gibt hier Werte größer 0 und kleiner 1 aus – genauer gesagt: Immer denselben Wert! Innerhalb eines awk-Aufrufs gibt rand bei jedem Aufruf eine andere Zufallszahl aus, aber jeder erneute awk-Aufruf bringt wieder dieselben Zahlen zu Tage.

awk 'BEGIN{print rand();print rand()};'
awk 'BEGIN{print rand();print rand()};'

würde also zweimal dieselben beiden unterschiedlichen Zufallszahlen ausgeben. Um bei jedem Aufruf eine andere Zufallszahl zu generieren, kann die Funktion srand (s für seed) genutzt werden:

awk 'BEGIN{{srand();}print rand()}'

Über srand wird hier lediglich der Seed für rand gesetzt und die Ausgabe ist nun wirklich zufällig – jedoch nur auf den ersten Blick. Schließlich wird hier kein expliziter Seed angegeben, so dass awk standardmäßig das aktuelle Datum nutzt.

Führt man den obigen Befehl innerhalb derselben Sekunden zweimal aus, bekommt man auch zweimal dasselbe Ergebnis. Die Lösung: Auch der Seed selbst muss bei jedem Aufruf zufällig gesetzt werden, was wieder das schon bekannte $RANDOM erledigt:

awk -v seed=$RANDOM 'BEGIN{{srand(seed);}print rand()}'

Zufallsdezimalzahlen zwischen 0 und 1 sind gut und schön, aber in der Praxis sollen es wieder ganze Zahlen zwischen 1 und 10 sein:

awk -v min=1 -v max=10 -v seed=$RANDOM 'BEGIN{{srand(seed);}print int(min+rand()*(max-min+1))}'

Neu sind nun zum einen die Variablen für Minimum und Maximum. Zum anderen produziert die int-Funktion ganze Zahlen zwischen diesen beiden Werten.

Alles dem Zufall überlassen

Wann Sie nun welche Variante oder Kombination von Zufallswerkzeugen nutzen, hängt ganz von Fantasie und Einsatzzweck ab. Die awk-Lösung lässt sich sehr universell einsetzen, auch für das Mischen von Optionen – allerdings ist shuf da etwas handlicher.

Auch $RANDOM ist eine erfreulich einfache Variante für viele Alltagsaufgaben, aber eben nur in der Bash verfügbar! Wenn es um Sicherheit geht, ist vermutlich nach wie vor die direkte Arbeit mit „/dev/random“ die beste Variante, da sie Zufallsdaten ohne weitere Tools liefert – das od-Werkzeug sorgt schließlich nur für eine menschenlesbare Formatierung.

(ID:48491851)