0

In bash I need to run a script that loops from i=1 to i=99999999 but it always run out of memory. Is there any workaround? or is there a max value for i?

first=1
last=99999999
randomString="CXCXQOOPSOIS"

for val in $( seq $first $last )
do
  padVal=$( printf "%010d\n" $val )
  hash=$( echo -n $randomString$padVal | md5sum )
  if [[ "$hash" =~ ^000000) ]]; then
    echo "Number: $val"  >> log_000000
    echo "$val added to log - please check."
  fi
done
πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
John Marston
  • 11,549
  • 7
  • 23
  • 18

3 Answers3

5

bash provides C-like syntax for loop:

first=1
last=99999999
randomString="CXCXQOOPSOIS"

for ((val=$first; val<$last; val++))
do
  padVal=$( printf "%010d\n" $val )
  hash=$( echo -n $randomString$padVal | md5sum )
  if [[ "$hash" =~ ^000000) ]]; then
    echo "Number: $val"  >> log_000000
    echo "$val added to log - please check."
  fi
done
oHo
  • 51,447
  • 27
  • 165
  • 200
1

Your seq command generates 100 million numbers (bar a couple) and requires 800 MiB or so of memory to hold just the list of digits (probably an under-estimate; each number might be held in a separate memory allocation, which might mean 8 bytes for a pointer and 16 bytes for the allocated block, which triples the storage space estimate).

You can improve things dramatically by using:

for millions in $(seq 0 99)
do
    for smallstuff in $(seq -f "%6.0f" 0 999999)
    do
        val="$millions$smallstuff"
        ...
    done
done

This dramatically reduces the amount of memory needed; the only issue to watch is that it tests 0 which your original code did not.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • what does the line for millions in $(seq 0 99) do? – John Marston Jan 09 '12 at 16:02
  • It generates strings 0 to 99 (one hundred strings of up to 2 characters each). Actually, you probably want to use: `for millions in "" $(seq 1 99)` to generate an empty prefix. Also, the inner loop generates leading zeroes on the small stuff; if that matters, you have to be a bit more careful, but the basic idea of breaking up the loop so that you don't generate 100 million strings at the start still stands. – Jonathan Leffler Jan 09 '12 at 16:37
1

If you still want to use seq => therefore separate seq and the loop using a pipe: |
This solution is more portable and can be used on other shells.
The memory print is still reduced, but this script requires to process two threads.

first=1
last=99999999
randomString="CXCXQOOPSOIS"

seq $first $last |
while read val
do
  padVal=$( printf "%010d\n" $val )
  hash=$( echo -n $randomString$padVal | md5sum )
  if [[ "$hash" =~ ^000000) ]]; then
    echo "Number: $val"  >> log_000000
    echo "$val added to log - please check."
  fi
done
oHo
  • 51,447
  • 27
  • 165
  • 200