$RANDOM
Nie wszystkie shelle wspierają generowanie losowych danych. W niektórych wystarczy użyć zmiennej $RANDOM, która za każdym razem zawiera losową liczbę. Przykładowo bash:
$ bash -c 'echo $RANDOM'
22139
Bash w trybie POSIX:
$ bash --posix -c 'echo $RANDOM'
14343
ksh:
$ ksh -c 'echo $RANDOM'
11421
Busybox (w Alpine Linux):
$ docker run -it --rm alpine
/ # ls -la $(command -v $0)
lrwxrwxrwx 1 root root 12 Nov 24 09:20 /bin/sh -> /bin/busybox
/ # echo $RANDOM
13196
ZSH:
$ zsh -c 'echo $RANDOM'
18535
Ale przykładowo w dash (zlinkowany do /bin/sh w Debian/Ubuntu) już to nie zadziała.
$ dash -c 'echo $RANDOM'
$
$ sh -c 'echo $RANDOM'
$
ash również tego nie implementuje:
$ ash -c 'echo $RANDOM'
$
Oczywiście - można zapytać: "kto używa ash?". Ale daleko szukać nie trzeba - logujemy się np. na router i sprawdzamy:
rtr $ echo $RANDOM
rtr $
rtr $ head -2 /etc/os-release
NAME="OpenWrt"
VERSION="19.07.7"
rtr $ echo $SHELL
/bin/ash
Systemów i urządzeń jest wiele. Jest cała rodzina systemów BSD i innych systemów Unix, jest Android, są systemy embedded i choćby mnóstwo rozwiązań na raspberrypi.
Zmienna $RANDOM nie znajduje się w specyfikacji POSIX. Nie jest to oczywiste, wystarczy spojrzeć na powyższy przykład "bash --posix", gdzie to rozszerzenie - mimo wszystko - działa. Jak sobie z tym poradzić?
Sposobów pewnie jest wiele, niemniej - nie są one intuicyjne. Jeśli mamy np. coreutils, to możemy użyć zawiłej komendy:
$ expr $(cat /dev/random | tr -dc '[:digit:]' | fold -w 8 | head -n 1)
96089464
Jeśli chcemy uzyskać liczbę, to powyższy przykład wyciąga dane z /dev/random, usuwa wszystko co nie jest cyfrą, zawija to do 8 znaków i pobiera tylko pierwszą linię. Całość oczywiście traktujemy "expr", aby uzyskać właśnie wartość liczbową.
expr
$ x=$(cat /dev/random | tr -dc '[:digit:]' | fold -w 8 | head -n 1)
$ y=$(expr $x + 1)
$ echo $x
39875261
$ echo $y
39875262
$ expr $y / 17
2345603
$ expr 2345603 \* 17 # !!!
39875251
$ test $y -eq $(expr 2345603 \* 17) && echo "OK" || echo "NOT OK"
NOT OK
$ expr 2 \* 2
4
/dev/random, /dev/urandom
/dev/random znajdziemy w każdym systemie klasy Unix zgodnym z POSIX.
/dev/urandom znajdziemy między innymi w Linux.
Obu możemy użyć w celu uzyskania wartości losowych. Przykładowo pobieramy 4 bajty:
$ dd if=/dev/random bs=4 count=1
*A��
Lub 10 ciągów tekstowych po 13 bajtów:
$ cat /dev/random | tr -dc '[:alnum:]' | fold -w 13 | head -n 10
MZIb47KmWbKdS
MCiiUbkayu6Ph
tTfvZrr3wfINz
7iFfjWoptpbdg
hvnP5SB70M6sq
2ZnsbW35YG4tO
MmIJG1wXnVRTX
v7reK60nRBlkY
JoTpVaBdLRedt
xYO1FDn6EGyWT
Możemy tak wygenerować np. losowe hasło (tutaj o długości 32 znaków /fold/):
$ passwd=$(cat /dev/urandom | tr -dc '[:alnum:][:punct:]' | fold -w 32 | head -n 1)
$ echo $passwd
,]w0[18PR[ABu{\[[2\UIr\*WPvVA#"g
uuidgen
Jeśli mamy do dyspozycji komendę uuidgen (w Debian pakiet uuid-runtime), to możemy generować dane typu UUID.
$ uuidgen
3f688742-1a1c-49d4-8d0a-350c4081095c
$ uuidgen -r
48be214c-2d0f-49e8-8938-67c80196652c
openssl rand
Jeśli mamy zainstalowany openssl, to zawiera on moduł "rand".
$ openssl rand --base64 32
bNarpISMYllpF57OPKlJqRv0/AF9CxgFhRWLPYvGi5Q=
$ openssl rand --base64 16
gPQ7NQisE8qgbqZeyS83WA==
$ openssl rand -hex 16
913a5095e511c284ade88f08a87b905f
$ openssl rand -hex 16
b8a57293511be096ed624393c615c685
$ openssl rand -hex 32
5cdca35831646ef3002b07b7a38b6dcca0b99863747e24e1feea471f19312074
Moglibyśmy również wykorzystać /dev/urandom w połączeniu z modułem openssl hashującym hasła (openssl passwd).
$ cat /dev/urandom | fold -w 12 | head -1 | openssl passwd -stdin
fUorK1vjHPX9A
$ cat /dev/urandom | fold -w 12 | head -1 | openssl passwd -stdin
PrNL4677tRVE2
$ cat /dev/urandom | fold -w 12 | head -1 | openssl passwd -apr1 -stdin
$apr1$TO5r5jkn$u4nXlcgy1BV6IGR8/VVxw0
$ cat /dev/urandom | fold -w 12 | head -1 | openssl passwd -apr1 -stdin
$apr1$imscoJsr$0/8l8QeM5iQf3YF3oprwL0
Sposobów na pewno jest więcej (można np. uruchomić jakiś interpreter), ale każdy z nich ma swoje jasne i ciemne strony (np. odczyt z /dev/random może się zablokować). POSIX specyfikuje m.in. funkcje (srand(), rand(), rand_r(), random()), a jednak zdaje się, że nie ma (?) uniwersalnej komendy, która po prostu wydaje losową wartość wylosowaną np. funkcją random().