There's no need for a bash loop calling awk multiple times, just loop within 1 call to awk, e.g. with GNU awk which handles the potential "too many open files" problem for you automatically:
awk '{
for (i=1; i<=21; i++) {
print $(i+2) > ("col"i".csv")
}
}' z.csv
and with any awk:
awk '{
for (i=1; i<=21; i++) {
close(out)
out = "col"i".csv"
print $(i+2) >> out
}
}' z.csv
If closing all of the files on every iteration causes a performance problem and you find you can have, say, 11 output files open at once without getting a "too many open files" error then you could do something like this:
awk '{
for (i=1; i<=21; i++) {
if (i>11) close(out)
out = "col"i".csv"
print $(i+2) >> out
}
}' z.csv
or slightly more efficiently but with a bit more code:
awk '{
for (i=1; i<=10; i++) {
print $(i+2) > ("col"i".csv")
}
for (; i<=21; i++) {
close(out)
out = "col"i".csv"
print $(i+2) >> out
}
}' z.csv