How to Find All Combinations of 4 Numbers Using Bash
Generating every possible combination of 4 numbers is a surprisingly common task — whether you're writing test data, exploring number patterns, solving a puzzle, or automating a security audit. Bash gives you several clean ways to do this, each with trade-offs depending on what "combination" actually means in your situation.
What "Combinations" Actually Means Here
Before writing a single line of Bash, it's worth getting precise about terminology, because the word combination gets used loosely:
- Permutations — order matters.
1234and4321are different results. - Combinations — order doesn't matter.
1234and4321are the same group. - With repetition — digits can repeat:
1112,9999, etc. - Without repetition — each digit used only once per sequence.
Most people searching for this topic want one of two things: either all 4-digit sequences from a set of digits (permutations with repetition, like generating every PIN from 0000–9999), or unique groupings where order doesn't matter and digits don't repeat (true combinations). The Bash approach differs meaningfully between these two cases.
Generating All 4-Digit Sequences (0000–9999) 🔢
If your goal is every possible 4-digit number — including leading zeros — a simple nested loop or brace expansion handles it efficiently.
Using nested loops:
for i in {0..9}; do for j in {0..9}; do for k in {0..9}; do for l in {0..9}; do echo "${i}${j}${k}${l}" done done done done This outputs all 10,000 combinations from 0000 to 9999. It's readable, portable across most Unix-like systems, and easy to pipe into other commands.
Using seq and printf for formatted output:
for i in $(seq -w 0 9999); do echo "$i" done The -w flag pads numbers with leading zeros, so 0001 stays four digits wide. This is more concise but slightly less flexible if you want to filter or transform mid-loop.
Generating True Combinations Without Repetition
If you're working from a specific set of numbers — say, {1, 2, 3, 4, 5, 6} — and want every group of 4 where order doesn't matter and values don't repeat, you need a different structure.
Bash approach using nested loops with index control:
nums=(1 2 3 4 5 6) len=${#nums[@]} for ((a=0; a<len-3; a++)); do for ((b=a+1; b<len-2; b++)); do for ((c=b+1; c<len-1; c++)); do for ((d=c+1; d<len; d++)); do echo "${nums[a]} ${nums[b]} ${nums[c]} ${nums[d]}" done done done done Each inner loop starts one index ahead of the previous, which eliminates duplicates and enforces ascending order — the defining property of true combinations. From a set of 6 numbers, this produces 15 unique combinations (C(6,4) = 15).
Permutations With a Fixed Set of Digits
If you want every ordered arrangement of 4 digits from a set (where order matters but repetition isn't allowed), the loop logic changes slightly:
nums=(1 2 3 4) for a in "${nums[@]}"; do for b in "${nums[@]}"; do [[ "$b" == "$a" ]] && continue for c in "${nums[@]}"; do [[ "$c" == "$a" || "$c" == "$b" ]] && continue for d in "${nums[@]}"; do [[ "$d" == "$a" || "$d" == "$b" || "$d" == "$c" ]] && continue echo "${a}${b}${c}${d}" done done done done The continue statements skip values already used in the current sequence. With 4 unique digits, this produces 24 permutations (4! = 24).
Comparing the Approaches
| Goal | Method | Output Count (example) |
|---|---|---|
| All 4-digit PINs (0–9) | Nested loops or seq -w | 10,000 |
| True combinations, no repeat | Index-controlled loops | Depends on set size |
| All ordered permutations | Loops with equality checks | n! for n distinct digits |
| Combinations with repetition | Standard nested loops | n⁴ for n digits |
Practical Considerations That Affect Your Approach 💡
Output volume is a real factor. Piping 10,000 lines to a file is trivial; running permutation logic across a large set can slow a script noticeably. If performance matters, awk or Python handle combinatorics faster than pure Bash loops for large inputs.
Portability is another variable. Bash arithmetic ((( ))) and arrays work in Bash 3.2+ and most Linux distributions, but if you're running this on a minimal embedded system or older macOS with /bin/sh, syntax compatibility matters.
What you do with the output shapes which method fits. If you're feeding results into another command, piping directly is cleaner than writing to a file first. If you need to store or process results, array-based collection inside the script may be more maintainable.
Readability vs. cleverness is a genuine trade-off in shell scripting. The nested loop approach is verbose but easy to audit. One-liners using printf tricks or process substitution are compact but harder to modify later.
When Bash Isn't the Right Tool
For large combination sets — say, permutations of 8+ numbers — Bash loops become slow and awkward. Tools like Python (with itertools.combinations and itertools.permutations), awk, or even perl handle these tasks with less code and significantly better performance. Bash shines for quick scripts, pipeline integration, and situations where you're already working in the shell environment.
The right method ultimately depends on your number set, how many results you expect, whether order and repetition matter in your specific case, and what environment you're running the script in — factors only your particular setup can answer.