This should be run on a physical machine; not on an emulated system or virtual env. Tune the if variable according to needs.
if=eth0
t="$(ntptime | grep "time " | head -n1 | awk '{print $2}')"
EUI_48="$(ip a s dev $if | grep "link/ether" | awk '{print $2}')"
EUI_64="$(ipv6calc --action prefixmac2ipv6 --in prefix+mac --out ipv6addr :: $EUI_48 | sed 's/^:://')"
conq="$(echo "$t $EUI_64" | tr -d ".: ")"
sha1="$(for x in $(echo "$conq" | sed 's/\(..\)/\1 /g') ; do printf "\x${x}" ; done | sha1sum | awk '{print $1}' | tail -c 11 | sed 's/\(..\)/\1 /g')"
ULA="$(echo $sha1 | awk '{print "fd" $1 ":" $2 $3 ":" $4 $5 "::/48"}')"
echo "$t $EUI_48 $EUI_64 $conq=> $ULA"
By the way, ipv6calc is designed to produce the link local address this way:
ipv6calc --action prefixmac2ipv6 --in prefix+mac --out ipv6addr fe80:: $EUI_48
If you don't want to install ipv6calc, here is a function to convert EUI-48 into EUI-64: https://gist.github.com/mej/592da948323fc58455c1