I'm trying to generate a passphrase using a loop in my Bash script, but it seems to run only once. Here's the setup: I've got a variable that defines how many words I want to generate, and I'm using the `/usr/share/dict/words` file for my word list. In my function, I read words from this list using a while loop. The problem is that the loop doesn't seem to iterate beyond the first word. I've set `errexit` to terminate the script if any command fails, but I suspect that might be causing the loop to exit prematurely. Any insights on why this is happening?
3 Answers
The issue you’re facing is due to how Bash evaluates the exit status of commands. With `set -o errexit` enabled, if any command returns a non-zero status, the script will exit. In your case, when you do `((teller++))`, it returns the old value which is 0 initially. Bash treats 0 as an error, resulting in the script quitting after the first iteration. A quick fix would be to change your incrementation to `((++teller))`, which increments `teller` before the comparison, giving it a non-zero exit status. Try adjusting it this way!
Instead of shuffling the entire word list for passphrase generation (which can be inefficient), you could create a helper function to pick random words directly from the list. This way, you can allow for duplicates and improve the randomness of your passphrase. It would look something like this:
```bash
uniform_rng() {
local min=$((SRANDOM % $1))
local n=$SRANDOM
while (($n < $min)); do
n=$SRANDOM
done
echo $(($n % $1))
}
generate_passphrase() {
local n=0
local pass=()
local set_size=$(wc -l 0)); do
n=$(uniform_rng $set_size)
pass+=($(awk "NR==$n+1" $word_list))
((num_words--))
done
echo "${pass[@]}"
}
```
This will give you a more secure way to generate a passphrase.
You're onto something with the `errexit` setting. For your `teller++` expression, Bash treats the old value of `teller` (which is 0 on the first run) as an error, resulting in a loop termination after reading just one word. Instead, you could modify the while loop to use a conditional check like this: `while [ "${teller}" -lt "${num_words}" ]; do ...`. This allows for proper iteration without triggering the exit on increment!
Just a heads-up, using Bash arithmetic will also work better for conditions in this context. So you might consider using `while (( teller < num_words )); do ...` for better performance and readability!
Also, if your indentation is off, it might break the script visually. Make sure to format your code correctly for better clarity on how the loop is structured.

That's a great point! Also, to keep the loop cleaner, you could condense the logic into the while condition itself like this: `while (( teller++ < num_words )); do ...`. This way, the exit status is determined by the while condition and avoids premature exits.