8

I am trying to print a random n letter word, where I input n from the command line itself, but for some reason my script is giving me the same answer every time when using the same value for n.

#!/bin/bash                                                                                                                                       
num=$1
egrep "^.{$num}$" /usr/share/dict/words | head -n $RANDOM| tail -n 1

I am calling my script like:

$ bash var3.sh 5
étude             # always the same output when using 5 

$ bash var3.sh 3
zoo               # always the same output when using 3

where var3.sh is the name of my script and 5 is the length of the word I want to print randomly.

How do I get it to print a truly random word?

  • 3
    $RANDOM is pretty likely to be greater than the number of n-letter words for most values of n (95.7% of the time for n = 3 for me). – Michael Homer Jun 12 '18 at 8:46
  • Are you looking to print a random word completely, even if it isn't a word or print it randomly from the dict? – iZodiac Jun 12 '18 at 8:52
  • If your task is related to security (for example to create a password), you should use a tool made for that task. Otherwise, it is possible to use shuf or sort -R as suggested in the answers. You can also use $RANDOM, but in a more advanced way. All these tools produce results that can be predicted (they are not truly random), but they are fast and good enough for many purposes. – sudodus Jun 12 '18 at 9:16
  • 1
    Maybe they use this method (obligatory XKCD reference) – molnarm Jun 12 '18 at 14:53
20

It doesn't. But $RANDOM returns big numbers (between 0 and 32767) which, especially for words of limited lengths, shows the same result, as the head portion probably returns all the results of the grep (for 3, there are only 819 matches in my /usr/share/dict/words).

Better solution might be to shuffle the results:

egrep "^.{$num}$" /usr/share/dict/words | sort -R | tail -n 1

where -R means --random-sort (a GNU sort extension).

  • 16
    Why tail? That made sense in OP's script, but since you're shuffling you could as well use head, and then sort should be able to detect the broken pipe and not bother shuffling the rest of the lines. – Peter Taylor Jun 12 '18 at 16:01
  • @PeterTaylor probably a good tip. Using tail is just a habit of mine... In the end, I'd probably prefer even more the shuf -n1, which is one pipe less... – Jakub Lucký Jun 14 '18 at 11:45
19

A simple method to print an arbitrary num-letter word uses shuf:

egrep "^.{$num}$" /usr/share/dict | shuf -n1

The shuf command outputs a random permutation of the input, and the -n1 flag tells it to only output the first item from this result.

  • 3
    or grep -Ex ".{$num}". Or awk 'length == n' n="$num"'. – Stéphane Chazelas Jun 12 '18 at 9:14
5

As others have pointed out, the main issue with your code is that $RANDOM more often than not is going to be a value much larger than then number of words of a certain length.

Using awk only:

$ awk -v len="$num" 'length == len { word[i++]=$0 }
                     END { print word[int(i*rand())] }' /usr/share/dict/words
Bosniac

The program reads in all lines from the given file that are of a certain length. These are stored in the array words.

At the end, a random element from this array is selected and printed.

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.