Wszystkie możliwe kombinacje znaków i liczb

13

Chcę więc wygenerować wszystkie możliwe kombinacje małych i wielkich liter oraz cyfr, które mogą składać się z 5 znaków.

Możliwości: a..z, A..Z i 0..9.

Czy jest jakiś elegancki sposób na zrobienie tego w bashu?

ardevd
źródło
1
Czy chcesz ograniczyć się do ASCII lub skryptu łacińskiego? Co ze znakami diakrytycznymi, takimi jak akcenty (é, â ...)?
Stéphane Chazelas
Dzięki za kontynuację. Zaktualizowany oryginalny post dla wyjaśnienia.
ardevd
Czy to naprawdę musi być bash? Czy zrobi to język taki jak Perl czy awk?
terdon
1
Dlaczego więc nie po prostu wywołać Perla lub Pythona z bash? Zwłaszcza z tym perl, że jest bardzo łatwy w użyciu jako jednowarstwowy.
terdon
2
Próbujesz się nauczyć, czy chcesz tylko wynik? W drugim przypadku jest wiele programów, które wykonują takie zadania, jak John Ripper ( john) i tym podobne, co daje wiele możliwości.
YoMismo,

Odpowiedzi:

13

Oto rozwiązanie bash, które przyjmuje żądaną długość jako parametr (zrobiłbyś to permute 5w twoim przypadku):

#!/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"

Jest to jednak boleśnie powolne. Czy mogę polecić C? https://youtu.be/H4YRPdRXKFs?t=18s

#include <stdio.h>

//global variables and magic numbers are the basis of good programming
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;
}

Uruchom:

make CFLAGS=-O3 permute && time ./permute 5 >/dev/null #about 20s on my PC

Języki wysokiego poziomu są do kipienia brutalnym wymuszaniem (to w zasadzie to, co robisz).

PSkocik
źródło
@ Stéphane Chazelas Dziękuję bardzo za tę edycję. Pisałem brudne, ignorując „właściwe” cytowanie, ponieważ w tym przypadku nie jest potrzebne, ale jestem bardzo wdzięczny za skróty!
PSkocik
Próbowałem twojego bashrozwiązania. To bardzo miłe; Bardzo to lubię. Działało dobrze przez około 24 godziny, zanim zauważyłem, że mój system został całkowicie zamknięty. Próbowałem czegoś podobnego z `python; z podobnym wynikiem, chociaż był znacznie szybszy.
głosy,
8

W bashmożesz spróbować:

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}}

ale zajęłoby to wieczność i pochłonęło całą pamięć. Najlepiej byłoby użyć innego narzędzia, takiego jak 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"}}}}}'

Uważaj, to 6 x 62 5 bajtów, więc 5 496 796,992.

Możesz wykonać tę samą pętlę bash, ale bashponieważ jest to najwolniejsza powłoka na zachodzie, zajmie to wiele godzin:

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

(w moim systemie wydajność ta wynosi 700 kiB / s, a nie 20MiB / s przy perlekwiwalencie).

Stéphane Chazelas
źródło
Wydaje mi się również, że dodałoby to do odpowiedzi, gdybyś dodał sposób na wyprowadzenie go do pliku; może podczas generowania, aby nie zniszczył pamięci RAM, lub po tym, jak wszystko jest buforowane w pamięci RAM
Hellreaver
2
@Hellreaver, wszyscy piszą do pliku (na standardowe wyjście, do dowolnego pliku, na który jest otwarty; jeśli jest uruchamiany w terminalu, plik urządzenia jak /dev/pts/something; i można to zmienić za pomocą operatora przekierowania powłoki), nie pamięć, ale pierwszy buduje całe wyjście w pamięci przed wysłaniem (do pliku otwartego na standardowe wyjście).
Stéphane Chazelas
4

Oto sposób na zrobienie tego czysto w trybie bash bez konieczności gryzenia 5 GB pamięci:

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
G-Man mówi „Przywróć Monikę”
źródło
2

Ta wersja bash wciąż nie jest tak szybka jak Perl, ale jest około cztery razy szybsza niż pięć zagnieżdżonych pętli:

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
rici
źródło
1

Możesz użyć crunch(który jest dostępny przynajmniej w dystrybucjach Kali).

crunch 5 5 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890
użytkownik123456
źródło
1

Cóż ... elegancki ?, tak (tylko szybka próbka):

eval echo $(printf "%s" '{{a..z},{A..Z},{0..9}}'{,,} )

To pełne wyrażenie najprawdopodobniej zablokuje komputer:

eval echo $(printf "%s" '{{a..z},{A..Z},{0..9}}'{,,,,} )

Jedną z opcji nieblokujących jest użycie kilku pętli:

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"

Nazwij to tak:

./script 3 '{a..z} {A..Z} {0..9}'

Gdzie pierwszy argument to liczba znaków, a drugi to lista (oddzielone spacjami) użytych znaków.

To zbuduje zmienną ( loop) ze skryptem do uruchomienia, a ostatnia ewaluacja wykona ten skrypt. Na przykład dla:

$ ./script 5 '{a..z} {A..Z} {0..9}'

Wartość loopbędzie wynosić:

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;
NotAnUnixNazi
źródło
1

Gnu Parallel może wykonywać kombinacje patrz https://www.gnu.org/software/parallel/ Coś takiego:

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}
Christopher Barham
źródło