Generieren Sie einen zufälligen Dateinamen in der Unix-Shell

75

Ich möchte einen zufälligen Dateinamen in einer Unix-Shell generieren (sagen wir tcshell). Der Dateiname sollte aus zufälligen 32 Hex-Buchstaben bestehen, z.

c7fdfc8f409c548a10a0a89a791417c5

(zu dem ich hinzufügen werde, was notwendig ist). Der Punkt ist, dass es nur in der Shell möglich ist, ohne auf ein Programm zurückzugreifen.

RS
quelle
Siehe auch Wie erstelle ich eine UUID in Bash? .
Bis auf weiteres angehalten.

Antworten:

125

Angenommen, Sie arbeiten unter Linux, sollte Folgendes funktionieren:

cat /dev/urandom | tr -cd 'a-f0-9' | head -c 32

Dies ist nur dann pseudozufällig, wenn Ihrem System die Entropie ausgeht, aber (unter Linux) garantiert beendet wird. Wenn Sie wirklich zufällige Daten benötigen, wählen Sie /dev/randomstatt Katze /dev/urandom. Durch diese Änderung wird Ihr Code blockiert, bis genügend Entropie verfügbar ist, um eine wirklich zufällige Ausgabe zu erzeugen, sodass Ihr Code möglicherweise verlangsamt wird. Für die meisten Anwendungen ist die Ausgabe von /dev/urandomausreichend zufällig.

Wenn Sie unter OS X oder einem anderen BSD arbeiten, müssen Sie es wie folgt ändern:

cat /dev/urandom | env LC_CTYPE=C tr -cd 'a-f0-9' | head -c 32
fmark
quelle
1
@ LukeN: Deiner war gut. Und technisch ist nicht garantiert, dass diese Lösung endet :)
Präsident James K. Polk
1
Diese Lösung hat tatsächlich seltsame Dinge für mich getan, da nach dem eigentlichen zufälligen Hash ein weißes "%" -Zeichen angehängt wurde. Da sich meine Shell jedoch manchmal seltsam verhält, wollte ich dies vorher nicht schlecht aussehen lassen es wurde angenommen :)
LukeN
1
Ich habe dies auf einem Mac versucht, der ein / dev / urandom hat. Das Ausführen des Befehls in einer Bash-Shell verursacht einen Fehler - 'tr: Illegale Byte-Sequenz'
Gareth Stockwell
5
Ich denke, das Problem hier ist, dass BSD und Mac den String als Multibyte statt als Einzelbyte interpretieren. Ich habe keine Maschine, auf der ich das anprobieren kann. Melden Sie sich hier, wenn dies funktioniert: cat / dev / urandom | env LC_CTYPE = C tr -cd 'a-f0-9' | Kopf -c 32
Mark
2
Sieht aus wie eine andere nutzlose Verwendung von Katze :-)
Jens
42

Warum nicht den Unix-Befehl mktemp verwenden?

$ TMPFILE=`mktemp tmp.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX` &&  echo $TMPFILE
tmp.MnxEsPDsNUjrzDIiPhnWZKmlAXAO8983
Oleg Razgulyaev
quelle
5
In diesem Fall generieren Sie nicht nur die Zeichenfolge, sondern erstellen auch eine Datei.
Igor Chubin
5
Einige Implementierungen haben ein --dry-runFlag, um zu verhindern, dass eine Datei erstellt wird. Das eröffnet natürlich eine mögliche Rennbedingung.
Glenn Jackman
21

Ein Befehl, keine Pipe, keine Schleife:

hexdump -n 16 -v -e '/1 "%02X"' -e '/16 "\n"' /dev/urandom

Wenn Sie den Zeilenumbruch nicht benötigen, z. B. wenn Sie ihn in einer Variablen verwenden:

hexdump -n 16 -v -e '/1 "%02X"' /dev/urandom

Bei Verwendung von "16" werden 32 hexadezimale Ziffern generiert.

Bis auf weiteres angehalten.
quelle
Das ist fantastisch. Ich hätte nie gedacht, hexdumpdafür zu verwenden.
Avetisk
7

Wie Sie wahrscheinlich bei jeder Antwort bemerkt haben , müssen Sie im Allgemeinen "auf ein Programm zurückgreifen".

Ohne externe ausführbare Dateien in Bash und ksh:

string=''; for i in {0..31}; do string+=$(printf "%x" $(($RANDOM%16)) ); done; echo $string

in zsh:

string=''; for i in {0..31}; do string+=$(printf "%x" $(($RANDOM%16)) ); dummy=$RANDOM; done; echo $string

Ändern Sie die Kleinbuchstaben xin der Formatzeichenfolge in Großbuchstaben X, um die alphabetischen Hex-Zeichen in Großbuchstaben zu setzen.

Hier ist eine andere Möglichkeit, dies in Bash zu tun, jedoch ohne explizite Schleife:

printf -v string '%X' $(printf '%.2s ' $((RANDOM%16))' '{00..31})

Im Folgenden printfbeziehen sich "erste" und "zweite" auf die Reihenfolge, in der sie ausgeführt werden, und nicht auf die Reihenfolge, in der sie in der Zeile erscheinen.

Diese Technik verwendet die Klammererweiterung, um eine Liste von 32 Zufallszahlen mod 16 zu erstellen, gefolgt von einem Leerzeichen und einer der Zahlen im Bereich in geschweiften Klammern, gefolgt von einem anderen Leerzeichen (z 11 00. B. ). Für jedes Element dieser Liste entfernt das erste printfalle bis auf die ersten beiden Zeichen mit seiner Formatzeichenfolge ( %.2), wobei entweder einzelne Ziffern gefolgt von einem Leerzeichen oder zwei Ziffern übrig bleiben. Das Leerzeichen in der Formatzeichenfolge stellt sicher, dass zwischen jeder Ausgabenummer mindestens ein Leerzeichen steht.

Die Befehlssubstitution, die die erste enthält, printfwird nicht in Anführungszeichen gesetzt, sodass eine Wortaufteilung durchgeführt wird und jede Zahl printfals separates Argument an die zweite geht . Dort werden die Zahlen durch die Formatzeichenfolge in hexadezimal %Xkonvertiert und ohne Leerzeichen aneinander angehängt (da die Formatzeichenfolge keine enthält). Das Ergebnis wird in der genannten Variablen gespeichert string.

Wenn printfmehr Argumente empfangen werden, als die Formatzeichenfolge berücksichtigt, wird das Format nacheinander auf jedes Argument angewendet, bis alle verbraucht sind. Wenn weniger Argumente vorhanden sind, wird die nicht übereinstimmende Formatzeichenfolge (Teil) ignoriert, dies gilt jedoch in diesem Fall nicht.

Ich habe es in Bash 3.2, 4.4 und 5.0-alpha getestet. In zsh (5.2) oder ksh (93u +) funktioniert dies jedoch nicht, da die Klammererweiterung RANDOMin diesen Shells nur einmal ausgewertet wird.

Beachten Sie, dass aufgrund der Verwendung des Mod-Operators für einen Wert zwischen 0 und 32767 die Verteilung der Ziffern mithilfe der Snippets verzerrt sein kann (ganz zu schweigen von der Tatsache, dass die Zahlen in erster Linie pseudozufällig sind ). Da wir jedoch Mod 16 verwenden und 32768 durch 16 teilbar ist, ist dies hier kein Problem.

In jedem Fall ist der richtige Weg, dies zu tun, mktempwie in der Antwort von Oleg Razgulyaev .

Bis auf weiteres angehalten.
quelle
1
Vielen Dank, Dennis, es hat mir geholfen. Ich habe die Zeichenfolge einfach so eingestellt, dass sie leer ist, sodass die Länge der Zeichenfolge immer wie erwartet ist und jedes Mal angehängt wird, während dasselbe Skript erneut ausgeführt wird. RandomId = $ (string = ""; for i in {0..5}; do string + = $ (printf "% x" $ (($ RANDOM% 16))); done; echo $ string); Echo "Zufälliger ID-Wert ist $ {randomId}"
Santosh Kumar Arjunan
@ SantoshKumarA: Vielen Dank für den Hinweis, dass die Variable initialisiert werden muss. Das habe ich meiner Antwort hinzugefügt. Ich füge noch eine weitere interessante Technik und eine Erklärung dafür hinzu.
Bis auf weiteres angehalten.
5

In zsh getestet, sollte mit jeder BASH-kompatiblen Shell funktionieren!

#!/bin/zsh

SUM=`md5sum <<EOF
$RANDOM
EOF`

FN=`echo $SUM | awk '// { print $1 }'`

echo "Your new filename: $FN"

Beispiel:

$ zsh ranhash.sh
Your new filename: 2485938240bf200c26bb356bbbb0fa32
$ zsh ranhash.sh
Your new filename: ad25cb21bea35eba879bf3fc12581cc9
LukeN
quelle
1
Dadurch werden nur 32768 eindeutige Dateinamen generiert und nicht 16^32.
Bis auf weiteres angehalten.
Dies funktioniert auch nur auf Systemen, die eine md5sumBinärdatei enthalten. FreeBSD und OSX haben beispielsweise eine, /sbin/md5die dem gleichen Zweck dient, jedoch unterschiedliche Optionssyntax verwendet.
Ghoti
4

Noch ein anderer Weg [tm].

R=$(echo $RANDOM $RANDOM $RANDOM $RANDOM $RANDOM | md5 | cut -c -8)
FILENAME="abcdef-$R"
reto
quelle
Das auf den meisten Systemen verfügbare Programm ist md5sum. Ich mag diese Lösung, denn wenn Sie keine hohe Zufälligkeit benötigen, können Sie $ RANDOM nur einmal verwenden, was diese Lösung zur kürzesten und am besten lesbaren Lösung macht.
a) Wenn Sie md5sum haben, können Sie md5 durch md5sum ersetzen und den Schnitt entsprechend aktualisieren. b) Die Verwendung von nur einem $ RANDOM ist gefährlich. Es werden nur Werte zwischen 0 und 32767 erzeugt, die je nach Anwendung viel zu kurz sind.
Reto
3

Diese Antwort ist fmarks sehr ähnlich, daher kann ich sie nicht wirklich würdigen, aber ich fand die Befehlskombinationen cat und tr ziemlich langsam und fand diese Version ziemlich viel schneller. Du brauchst Hexdump.

hexdump -e '/1 "%02x"' -n32 < /dev/urandom
srclosson
quelle
As. Verwenden Sie für -n1632 Zeichen.
Walf
3

Die erste Antwort ist gut, aber warum Gabel Katze, wenn nicht erforderlich.

tr -dc 'a-f0-9' < /dev/urandom | head -c32
Josiah DeWitt
quelle
1

Nimm 16 Bytes von /dev/random, konvertiere sie in hexadezimal, nimm die erste Zeile, entferne die Adresse, entferne die Leerzeichen.

head /dev/random -c16 | od -tx1 -w16 | head -n1 | cut -d' ' -f2- | tr -d ' '

Angenommen, "ohne auf ein Programm zurückzugreifen" bedeutet natürlich "nur Programme verwenden, die leicht verfügbar sind".

Thomas
quelle
Ich habe das ausprobiert. Es ist viel langsamer als die anderen Lösungen, funktioniert aber.
Aarona
Ja, es ist eine ziemlich lange Pfeife für so etwas Einfaches. Ich hatte auf weitere Flaggen gehofft od, konnte es aber nicht dazu bringen, das zu tun, was ich wollte. Die Antwort von fmark ist zwar schneller, verarbeitet jedoch möglicherweise schneller.
Thomas
1
Es wäre schneller, wenn es / dev / urandom anstelle von / dev / random verwenden würde
fmark
fmark ist richtig, es könnte der / dev / random Teil hier sein, weil / dev / random ein wirklich zufälliger Zahlengenerator ist und blockiert, wenn es keine Entropie mehr gibt :)
LukeN
Diese Lösung ist definitiv die tragbarste! Es funktioniert auch auf der neuesten Android (außer Sie sollten ersetzen -w16mit -N16).
user1643723
1

Wir hoffen, diesem Thema eine (vielleicht) bessere Lösung hinzufügen zu können.

Hinweis: Dies funktioniert nur mit bash4und einigen Implementierungen von mktemp(zum Beispiel der GNU).

Versuche dies

fn=$(mktemp -u -t 'XXXXXX')
echo ${fn/\/tmp\//}

Dieser ist doppelt so schnell wie head /dev/urandom | tr -cd 'a-f0-9' | head -c 32und achtmal so schnell wie cat /dev/urandom | tr -cd 'a-f0-9' | head -c 32.

Benchmark:

Mit mktemp:

#!/bin/bash
# a.sh
for (( i = 0; i < 1000; i++ ))
do
    fn=$(mktemp -u -t 'XXXXXX')
    echo ${fn/\/tmp\//} > /dev/null
done

time ./a.sh 
./a.sh  0.36s user 1.97s system 99% cpu 2.333 total

Und der andere:

#!/bin/bash
# b.sh
for (( i = 0; i < 1000; i++ ))
do
    cat /dev/urandom | tr -dc 'a-zA-Z0-9' | head -c 32 > /dev/null
done

time ./b.sh 
./b.sh  0.52s user 20.61s system 113% cpu 18.653 total
罗泽轩
quelle
1

Wenn Sie unter Linux arbeiten, ist Python vorinstalliert. Sie können sich also für etwas Ähnliches wie das Folgende entscheiden:

python -c "import uuid; print str(uuid.uuid1())"

Wenn Ihnen die Bindestriche nicht gefallen, verwenden Sie die Ersetzungsfunktion wie unten gezeigt

python -c "import uuid; print str(uuid.uuid1()).replace('-','')"
Thyag
quelle
1

uuidgengeneriert genau dies, außer Sie müssen Bindestriche entfernen. Daher fand ich, dass dies der eleganteste (zumindest für mich) Weg ist, dies zu erreichen. Es sollte unter Linux und OS X sofort funktionieren.

uuidgen | tr -d '-'
Danran
quelle
0

Sie können auch den Befehl date wie folgt ausführen:

date +%S%N

Liest die Nicht-Sekunden-Zeit und das Ergebnis fügt viel Zufälligkeit hinzu.

user3174711
quelle
0

Wenn Sie opensslin Ihrem System haben, können Sie es zum Generieren von zufälligen Hex- -base64Strings (auch es kann sein ) mit definierter Länge verwenden. Ich fand es ziemlich einfach und in Cron in One-Line-Jobs verwendbar.

 openssl rand -hex 32
 8c5a7515837d7f0b19e7e6fa4c448400e70ffec88ecd811a3dce3272947cb452
Alex
quelle