So I want to do generate all possible combinations of lower and upper case characters and numbers that can make up a 5 character string.
Possibilities: a..z, A..Z and 0..9.
Is there any elegant way of doing this in bash at all?
Here's a bash solution that takes the desired length as parameter (you'd do permute 5 in your case):
#!/bin/bash
charset=({a..z} {A..Z} {0..9})
permute(){
(($1 == 0)) && { echo "$2"; return; }
for char in "${charset[@]}"
do
permute "$((${1} - 1 ))" "$2$char"
done
}
permute "$1"
It's painfully slow, though. Dare I recommend C? https://youtu.be/H4YRPdRXKFs?t=18s
#include <stdio.h>
const char* charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
char buffer[50];
void permute(int level) {
const char* charset_ptr = charset;
if (level == -1){
puts(buffer);
} else {
while(buffer[level] = *charset_ptr++) {
permute(level - 1);
}
}
}
int main(int argc, char **argv)
{
int length;
sscanf(argv[1], "%d", &length);
//Must provide length (integer < sizeof(buffer)==50) as first arg;
//It will crash and burn otherwise
buffer[length] = '\0';
permute(length - 1);
return 0;
}
Run it:
make CFLAGS=-O3 permute && time ./permute 5 >/dev/null #about 20s on my PC
High-level languages suck at brute-forcing (which is basically what you're doing).
bash solution. It's very nice; I like it a lot. It worked well for about 24 hours or so before I noticed my system was completely locked up. Tried something similar with `python; with a similar result, although it was considerably quicker.
In bash, you could try:
printf "%s\n" {{a..z},{A..Z},{0..9}}{{a..z},{A..Z},{0..9}}{{a..z},{A..Z},{0..9}}{{a..z},{A..Z},{0..9}}{{a..z},{A..Z},{0..9}}
but that would take forever and use-up all your memory. Best would be to use another tool like perl:
perl -le '@c = ("A".."Z","a".."z",0..9);
for $a (@c){for $b(@c){for $c(@c){for $d(@c){for $e(@c){
print "$a$b$c$d$e"}}}}}'
Beware that's 6 x 625 bytes, so 5,496,796,992.
You can do that same loop in bash, but bash being the slowest shell in the west, that's going to take hours:
export LC_ALL=C # seems to improve performance by about 10%
shopt -s xpg_echo # 2% gain (against my expectations)
set {a..z} {A..Z} {0..9}
for a do for b do for c do for d do for e do
echo "$a$b$c$d$e"
done; done; done; done; done
(on my system, that outputs at 700 kiB/s as opposed to 20MiB/s with the perl equivalent).
/dev/pts/something; and you can change that with shell redirection operator), not memory, but the first one builds the whole output in memory before outputting it (to the file open on stdout).
You can use crunch (which is available at least on Kali distributions).
crunch 5 5 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890
Here's a way to do it purely in bash without having to chomp 5 GB of memory:
for c1 in {A..Z} {a..z} {0..9}
do
for c2 in {A..Z} {a..z} {0..9}
do
for c3 in {A..Z} {a..z} {0..9}
do
for c4 in {A..Z} {a..z} {0..9}
do
for c5 in {A..Z} {a..z} {0..9}
do
printf "%s\n" "$c1$c2$c3$c4$c5"
done
done
done
done
done
Gnu Parallel can do combinations see https://www.gnu.org/software/parallel/ Something like this:
parallel echo ::: {a..z} {A..Z} {0..9} ::: {a..z} {A..Z} {0..9} ::: {a..z} {A..Z} {0..9} ::: {a..z} {A..Z} {0..9} ::: {a..z} {A..Z} {0..9}
This bash version is still not as fast as Perl but it's about four times as fast as five nested loops:
printf -vtwo "%s " {{a..z},{A..Z},{0..9}}{{a..z},{A..Z},{0..9}}
for three in {{a..z},{A..Z},{0..9}}{{a..z},{A..Z},{0..9}}{{a..z},{A..Z},{0..9}}; do
printf "$three%s\n" $two;
done
Well ... elegant?, yes (just a fast sample):
eval echo $(printf "%s" '{{a..z},{A..Z},{0..9}}'{,,} )
This full expression most likely will block your computer:
eval echo $(printf "%s" '{{a..z},{A..Z},{0..9}}'{,,,,} )
One non-blocking option is to use several loops:
nl=$'\n'; tab=$'\t'
n=${1:-3}
eval set -- "$2"
eval "varnames=($(echo {a..z}))"
for i in "${varnames[@]:0:$n}"; do
header+='for '"$i"' do '
middle+='$'"$i"
traile+="done; "
done
loop="${header}${nl} printf %s \"$middle\";${nl}$traile"
#echo "$loop"
eval "$loop"
Call it like:
./script 3 '{a..z} {A..Z} {0..9}'
Where the first argument is the number of characters and the second is the list (space separated) of characters used.
That will build a variable (loop) with the script to run and the last eval will execute that script. For example for:
$ ./script 5 '{a..z} {A..Z} {0..9}'
The value of loop will be:
for a do for b do for c do for d do for e do
echo "$a$b$c$d$e";
done; done; done; done; done;
Easy to extend to more chars, and does not require compilation:
perl -e 'sub p { if(not @_) { print @p,"\n"; } else { $a=shift; for (@$a) { push @p,$_; p(@_); pop @p; } }} $b=["a".."z","A".."Z",0..9]; p($b,$b,$b,$b,$b)'
#!/bin/bash
eval `for i in $(seq $1) ; do
echo "for c$i in {a..z} {A..Z} {0..9} ; do"
done
echo -n "echo "
for i in $(seq $1) ; do
echo -n '$c'$i
done
echo ' ;'
for i in $(seq $1) ; do
echo 'done ;'
done`
./npermuter <some number>
perl, it is very easy to use it as a one-liner.john) and the like, which will give you plenty of possibilities.