Trouble generating random hex numbers using /dev/urandom in bash - bash

I'm trying to generate a random hex number with an specified length ($length) using the following command:
head -c $length /dev/urandom | xxd -p -u -c $length | tr -d '[:space:]\\'
I've noticed that head -c $length /dev/urandom actually prints the double of the $length value. So if $length=1 the output will be 2 characters long. How can I fix this?
Also why does the input number on xxd -p -u -c $length seems not to affect on the output? meaning I can use either:
head -c 4 /dev/urandom | xxd -p -u -c 20000 | tr -d '[:space:]\\'
or
head -c 4 /dev/urandom | xxd -p -u -c 4 | tr -d '[:space:]\\'
and it will print 8 characters in both cases.

Related

Syntax error while trying to generate a 2048bit long prime number

I'm trying to generate a 2048 bit long prime number, this is my code so far:
#!/bin/bash
generate_random() {
hex=$(head -c 256 /dev/urandom | xxd -p)
bc <<< "ibase=16; $hex"
}
p=$(generate_random)
echo "$p"
While running the script I get (standard_in) 1: syntax error followed by random zeroes.
Anyone knows what is causing this error and how can I fix it? I've tried with bash -x, but it doesn't add any useful information.
First, bc understands only upper-case letters as hex digits (at least by default). Second, you have separators in your xxd output, so you generate multiple numbers with bc later.
This should work:
#!/bin/bash
generate_random() {
hex=$(head -c 256 /dev/urandom | xxd -p -u | tr -d '\n')
bc <<< "ibase=16; $hex"
}
p=$(generate_random)
echo "$p"
-u flag to xxd instructs it to output upper-case letters as digits, and tr removes separators.
Example output:
84404284040092528807148386035025161100484110236893077703095592941720\
00537078513504880246726730474236368181068985417211434943913923235822\
01284401417146606673073772989889733010524123703686975444423088406509\
44767677616371794606797386146855833950295071249000795855185540560405\
62673903614333076371092344026999031152809898928396395497832309795471\
93897215963003601022703133486344387720277877558264139632520964120681\
97764906669023878701319760947789227343517474218584987497204300184084\
62846775760153647010072072799120566180042021620262646969602253704108\
06274157727080642084167983313757899766696995668747042179553171962777\
5716
To remove newline separators and backslashes, you can do
p_joined=$(echo "$p" | sed -z 's=\\\n==g')
echo "$p_joined"
instead.
An alternative way might be
printf -v hex '%s' $(od -v -An -N256 -x /dev/urandom)
read dec < <(bc <<< "ibase=16; ${hex^^}")
echo $dec

Wants to generate a random password on Linux server of length 15 characters

String must contain at least 3 uppercase, 3 lowercase, 3 digits and at least 3 special characters.
I searched a lot, but not able to get the required solution.
head /dev/urandom | tr -dc 'A-Za-z0-9-_' | head -c15
This is what I found, but I am able to get the random combination of all.
What i needed is it must output at least 3 characters from each set.
Try this script:
#!/bin/sh
export LC_ALL=C
upp=$(tr -dc 'A-Z' </dev/urandom | head -c3)
low=$(tr -dc 'a-z' </dev/urandom | head -c3)
dig=$(tr -dc '0-9' </dev/urandom | head -c3)
spe=$(tr -dc '!-/' </dev/urandom | head -c3)
res=$(tr -dc '!-}' </dev/urandom | head -c3)
echo "$upp$low$dig$spe$res"
First, the statement export LC_ALL=C makes sure that we are using just plain ASCII. This eliminates potential issues associated with unicode characters.
Next, the variable upp is assigned to 3 upper-case characters. Similarly, low gets three lower-case, $dig gets three digits, spe gets three special characters, and res gets 3 random characters. The echo statement combines all four variables ands prints them.
The above prints the upper case characters first, lower case second, etc. If you want this order mixed up, replace the last line above with:
echo "$upp$low$dig$spe$res" | sed 's/./&\n/g' | shuf | tr -d '\n'
This script uses openssl-rand to get strings of 15 letters/numbers/special characters until the loop breaks when all conditions are met and the password is printed.
#!/bin/bash
until [ -z "$pw+x" ]
do
pw=$(openssl rand -base64 32 | cut -c1-15) &&\
[[ $(sed "s/[^[:upper:]]//g" <<< $pw | wc -c) -gt 3 ]] &&\
[[ $(sed "s/[^[:lower:]]//g" <<< $pw | wc -c) -gt 3 ]] &&\
[[ $(sed "s/[^0-9]//g" <<< $pw | wc -c) -gt 3 ]] &&\
[[ $(sed "s/[[:alnum:]]//g" <<< $pw | wc -c) -gt 3 ]] && break
done
echo "$pw"

How to get a random string of 32 hexadecimal digits through command line?

I'd like to put together a command that will print out a string of 32 hexadecimal digits. I've got a Python script that works:
python -c 'import random ; print "".join(map(lambda t: format(t, "02X"), [random.randrange(256) for x in range(16)]))'
This generates output like:
6EF6B30F9E557F948C402C89002C7C8A
Which is what I need.
On a Mac, I can even do this:
uuidgen | tr -d '-'
However, I don't have access to the more sophisticated scripting languages ruby and python, and I won't be on a Mac (so no uuidgen). I need to stick with more bash'ish tools like sed, awk, /dev/random because I'm on a limited platform. Is there a way to do this?
If you have hexdump then:
hexdump -vn16 -e'4/4 "%08X" 1 "\n"' /dev/urandom
should do the job.
Explanation:
-v to print all data (by default hexdump replaces repetition by *).
-n16 to consume 16 bytes of input (32 hex digits = 16 bytes).
4/4 "%08X" to iterate four times, consume 4 bytes per iteration and print the corresponding 32 bits value as 8 hex digits, with leading zeros, if needed.
1 "\n" to end with a single newline.
If you are looking for a single command and have openssl installed, see below. Generate random 16 bytes (32 hex symbols) and encode in hex (also -base64 is supported).
openssl rand -hex 16
There three ways that I know of:
#!/bin/bash
n=16
# Read n bytes from urandom (in hex):
xxd -l "$n" -p /dev/urandom | tr -d " \n" ; echo
od -vN "$n" -An -tx1 /dev/urandom | tr -d " \n" ; echo
hexdump -vn "$n" -e ' /1 "%02x"' /dev/urandom ; echo
Use one, comment out the other two.
Try:
xxd -u -l 16 -p /dev/urandom
Example output:
C298212CD8B55F2E193FFA16165E95E3
And to convert it back to binary:
echo -n C298212CD8B55F2E193FFA16165E95E3 | xxd -r -p
Here are a few more options, all of which have the nice property of providing an obvious and easy way to directly select the length of the output string. In all the cases below, changing the '32' to your desired string length is all you need to do.
#works in bash and busybox, but not in ksh
tr -dc 'A-F0-9' < /dev/urandom | head -c32
#works in bash and ksh, but not in busybox
tr -dc 'A-F0-9' < /dev/urandom | dd status=none bs=1 count=32
#works in bash, ksh, AND busybox! w00t!
tr -dc 'A-F0-9' < /dev/urandom | dd bs=1 count=32 2>/dev/null
EDIT: Tested in different shells.
If you want to generate output of arbitrary length, including even/odd number of characters:
cat /dev/urandom | hexdump --no-squeezing -e '/1 "%x"' | head -c 31
Or to maximize efficiency over readability/composeability:
hexdump --no-squeezing -e '/1 "%x"' -n 15 /dev/urandom
Here is a version not using dev/random:
awk -v len=32 'BEGIN {
srand('$RANDOM');
while(len--) {
n=int(rand()*16);
printf("%c", n+(n>9 ? 55 : 48));
};}'
you can also use od command like this
od -N32 -x < /dev/urandom | head -n1 | cut -b9- | sed 's/ //gi'
good luck

Generate random passwords in shell with one special character

I have the following code:
</dev/urandom tr -dc 'A-Za-z0-9##$%&_+=' | head -c 16
which is randomly generating passwords perfectly.
I want two changes:
It should only contain one special character listed above
It should choose a random length
I tried with length = $(($RANDOM%8+9))
then putting length as
</dev/urandom tr -dc 'A-Za-z0-9##$%&_+=' | head -c$length
but got no positive result.
#! /bin/bash
chars='##$%&_+='
{ </dev/urandom LC_ALL=C grep -ao '[A-Za-z0-9]' \
| head -n$((RANDOM % 8 + 9))
echo ${chars:$((RANDOM % ${#chars})):1} # Random special char.
} \
| shuf \
| tr -d '\n'
LC_ALL=C prevents characters like ř from appearing.
grep -o outputs just the matching substring, i.e. a single character.
shuf shuffles the lines. I originally used sort -R, but it kept the same characters together (ff1#22MvbcAA).
## Objective: Generating Random Password:
function random_password () {
[[ ${#1} -gt 0 ]] && { local length=${1}; } || { local length=16; }
export DEFAULT_PASSWORDLENGTH=${length};
export LC_CTYPE=C;
local random="$(
tr -cd "[:graph:]" < /dev/urandom \
| head -c ${length} \
| sed -e 's|\`|~|g' \
-e 's|\$(|\\$(|g';
)";
echo -e "${random}";
return 0;
}; alias random-password='random_password';
$ random-password 32 ;
)W#j*deZ2#eMuhU4TODO&eu&r)&.#~3F
# Warning: Do not consider these other options
# date +%s | sha256sum | base64 | head -c 32 | xargs -0;
# Output: MGFjNDlhMTE2ZWJjOTI4OGI4ZTFiZmEz
# dd if=/dev/urandom count=200 bs=1 2>/dev/null \
# | tr -cd "[:graph:]" \
# | cut -c-${length} \
# | xargs -0;
# Output: AuS*D=!wkHR.4DZ_la

How to assign output of multiple shell commmands to variable when using tee?

I want to tee and get the results from multiple shell commands connected in the pipeline. I made a simple example to explain the point. Suppose I wanna count the numbers of 'a', 'b' and 'c'.
echo "abcaabbcabc" | tee >(tr -dc 'a' | wc -m) >(tr -dc 'b' | wc -m) >(tr -dc 'c' | wc -m) > /dev/null
Then I tried to assign the result from each count to a shell variable, but they all end up empty.
echo "abcaabbcabc" | tee >(A=$(tr -dc 'a' | wc -m)) >(B=$(tr -dc 'b' | wc -m)) >(C=$(tr -dc 'c' | wc -m)) > /dev/null && echo $A $B $C
What is the right way to do it?
Use files. They are the single most reliable solution. Any of the commands may need different time to run. There is no easy way to synchronize command redirections. Then most reliable way is to use a separate "entity" to collect all the data:
tmpa=$(mktemp) tmpb=$(mktemp) tmpc=$(mktemp)
trap 'rm "$tmpa" "$tmpb" "$tmpc"' EXIT
echo "abcaabbcabc" |
tee >(tr -dc 'a' | wc -m > "$tmpa") >(tr -dc 'b' | wc -m > "$tmpb") |
tr -dc 'c' | wc -m > "$tmpc"
A=$(<"$tmpa")
B=$(<"$tmpb")
C=$(<"$tmpc")
rm "$tmpa" "$tmpb" "$tmpc"
trap '' EXIT
Second way:
You can prepend the data from each stream with a custom prefix. Then sort all lines (basically, buffer them) on the prefix and then read them. The example script will generate only a single number from each process substitution, so it's easy to do:
read -r A B C < <(
echo "abcaabbcabc" |
tee >(
tr -dc 'a' | wc -m | sed 's/^/A /'
) >(
tr -dc 'b' | wc -m | sed 's/^/B /'
) >(
tr -dc 'c' | wc -m | sed 's/^/C /'
) >/dev/null |
sort |
cut -d' ' -f2 |
paste -sd' '
)
echo A="$A" B="$B" C="$C"
Using temporary files with flock to synchronize the output of child processes could look like this:
tmpa=$(mktemp) tmpb=$(mktemp) tmpc=$(mktemp)
trap 'rm "$tmpa" "$tmpb" "$tmpc"' EXIT
echo "abcaabbcabc" |
(
flock 3
flock 4
flock 5
tee >(
tr -dc 'a' | wc -m |
{ sleep 0.1; cat; } > "$tmpa"
# unblock main thread
flock -u 3
) >(
tr -dc 'b' | wc -m |
{ sleep 0.2; cat; } > "$tmpb"
# unblock main thread
flock -u 4
) >(
tr -dc 'c' | wc -m |
{ sleep 0.3; cat; } > "$tmpc"
# unblock main thread
flock -u 5
) >/dev/null
# wait for subprocesses to finish
# need to re-open the files to block on them
(
flock 3
flock 4
flock 5
) 3<"$tmpa" 4<"$tmpb" 5<"$tmpc"
) 3<"$tmpa" 4<"$tmpb" 5<"$tmpc"
A=$(<"$tmpa")
B=$(<"$tmpb")
C=$(<"$tmpc")
declare -p A B C
You can use this featured letter frequency analysis
#!/usr/bin/env bash
declare -A letter_frequency
while read -r v k; do
letter_frequency[$k]="$v"
done < <(
grep -o '[[:alnum:]]' <<<"abcaabbcabc" |
sort |
uniq -c
)
for k in "${!letter_frequency[#]}"; do
printf '%c = %d\n' "$k" "${letter_frequency[$k]}"
done
Output:
c = 3
b = 4
a = 4
Or to only assign $A, $B and $C as in your example:
#!/usr/bin/env bash
{
read -r A _
read -r B _
read -r C _
}< <(
grep -o '[[:alnum:]]' <<<"abcaabbcabc" |
sort |
uniq -c
)
printf 'a=%d\nb=%d\nc=%d\n' "$A" "$B" "$C"
grep -o '[[:alnum:]]': split each alphanumeric character on its own line
sort: sort lines of characters
uniq -c: count each instance and output count and character for each
< <( command group; ): the output of this command group is for stdin of the command group before
If you need to count occurrence of non-printable characters, newlines, spaces, tabs, you have to make all these commands output and deal with null delimited lists. It can sure be done with the GNU versions of these tools. I let it to you as an exercise.
Solution to the count arbitrary characters except null:
As demonstrated, works also with Unicode.
#!/usr/bin/env bash
declare -A character_frequency
declare -i v
while read -d '' -r -N 8 v && read -r -d '' -N 1 k; do
character_frequency[$k]="$v"
done < <(
grep --only-matching --null-data . <<<$'a¹bc✓ ✓\n\t\t\u263A☺ ☺ aabbcabc' |
head --bytes -2 | # trim the newline added by grep
sort --zero-terminated | # sort null delimited list
uniq --count --zero-terminated # count occurences of char (null delim)
)
for k in "${!character_frequency[#]}"; do
printf '%q = %d\n' "$k" "${character_frequency[$k]}"
done
Output:
$'\n' = 1
$'\t' = 2
☺ = 3
\ = 7
✓ = 2
¹ = 1
c = 3
b = 4
a = 4

Resources