The output size is the product of the input sizes, and that multiplication catches people out. Two lists of 1,000 produce a million rows. Add a third dimension of 50 and you have fifty million.
Nested loops are the whole algorithm
Two nested loops, or in shell for a in $(cat list1); do for b in $(cat list2); do echo "$a $b"; done; done. The logic is trivial; the resource profile is not.
Generate lazily where you can
Materialising the full product into memory is the usual mistake. Generators and iterators emit combinations one at a time and keep memory flat regardless of output size. In SQL the same thing is a CROSS JOIN, and the database streams it rather than building it.
Cap before you generate
The practical guard is to multiply the input lengths first and refuse if the result exceeds a sane ceiling. Failing fast with a clear message beats producing a five-million-line file that no downstream step can process anyway.
Try it: Cartesian Product (Keyword Combiner) on SeoWolf's Notepad.