For start, we know how can we output random lines from a txt file:

perl -MList::Util -e 'print List::Util::shuffle <>' words.txt

But we need a general solution (perl is usually installed on every unix-like OS) to output 4 random words from a txt file, how can we do it?

$ cat words.txt

The "..." represents many other words.

Example output (without newline between them, but one unix newline is needed at the end, spaces should separate them):

$ perl SOMEMAGIC words.txt
gamma alpha delta beta

Using only perl, the shortest possible solution (oneliner).

It will be used as human memorable password generator: https://xkcd.com/936/

Since afaik perl is enough good to be named "random generator".

Example common english wordlist with 32768 unique lines: https://pastebin.ubuntu.com/23726760/

  • Did you mean to post this on Code Golf? Jan 1 '17 at 21:09
  • 6
    Otherwise, this feels like a general programming question (and so belonging on Stack Overflow), but it's also structured as a lazy "write my program" question that probably wouldn't be well-received. Jan 1 '17 at 21:10

Perl is actually not such a good tool for this because you need a third-party library to generate random numbers securely. The default random number generator (rand or anything else that uses the same source, such as List::Util::shuffle) is not suitable for anything related to security. If you want to use Perl, install Math::Random::Secure (and its dependencies) (perl -MCPAN -eshell and run install Math::Random::Secure).

perl -MMath::Random::Secure=rand -l -e '@words = <>; print map $words[rand(@words)], 1..4' words.txt

I suggest using Python instead. These days it's about as common as Perl, and its standard library is much better rounded. Yes, the code is longer, but short code is not in itself a sign of quality.

python -c 'import random, sys; rng = random.SystemRandom(); words = sys.stdin.readlines(); print " ".join([words[rng.randrange(0, len(words))].strip() for i in xrange(4)])' <words.txt

In a more readable multi-line form:

import random, sys
rng = random.SystemRandom()
words = sys.stdin.readlines()
print " ".join([words[rng.randrange(0, len(words))].strip() for i in xrange(4)])

Alternatively, if you have non-antique GNU coreutils, which is the case on non-embedded Linux and Cygwin, you can use the shuf utility. You need to pass a secure random generator with the --random-source option.

shuf --random-source=/dev/urandom -r -n 4 words.txt
  • testing the python oneliner, many-thanks!
    – halajoe
    Jan 2 '17 at 9:09
  • A note from my personal experience: some of the dictionaries you may find online use \r\n as the line separator, and in this case you need to replace :-1 with :-2 or else you’ll get an extremely weird result. Overall, it would have been much more reliable to use .strip() instead.
    – kirelagin
    Oct 5 '20 at 23:10

It sounds like you're not against non-Perl solutions, you just want something that works on most systems. In that case, the following uses only GNU core utils:

sort -R --random-source=/dev/urandom words.txt | head -n4 | paste -s -d' '

Modified to use shuf, based on Gilles' suggestion:

shuf -n 4 -r words.txt | paste -s -d' '
  • 1
    If you're going to rely on GNU coreutils, you might as well use shuf instead of this complex, slow pipeline. Using sort even introduces a small undesirable bias because it forbids repetition. Jan 1 '17 at 23:41
  • You need to pass --random-source with shuf as well. The default RNG is not seeded reliably. Jan 2 '17 at 0:58
  • "sort -R" is not random! try it with a testfile like: printf '1\n1\n2\n2\n' > a.txt
    – halajoe
    Jan 2 '17 at 9:04
  • 1
    @halajoe sort -R is random (but with poorly-seeded entropy unless --random-source points to a good RNG) for inputs without duplicates. Jan 2 '17 at 12:43
  • 1
    You can use xargs without options instead of the paste invocation. Example using a secure random source and the words file: shuf --head-count=4 --random-source=/dev/urandom /usr/share/dict/words | xargs
    – Claudio
    Jul 14 '21 at 20:46

(ignoring the security problem mentioned by @Gilles)

 perl -0nE '@a=split; say join(" ", map{ $a[rand @a] } 1..4)'

or if you like CamelCase...

 perl -0nE '@a=split; say map{ ucfirst $a[rand @a] } 1..4'
  • Since you're mentioning "security problems", maybe you should point these out? It would help others if you additionally explained what the code is doing.
    – Kusalananda
    Jan 2 '17 at 12:39
  • 2
    @Kusalananda The security problem is the one I mention in my answer: Perl's rand is not a secure RNG. Jan 2 '17 at 12:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.