I don't see a shell version, so below is mine (well...it is not strictly shell, as it uses sort, uniq, grep, sed, and comm). It's written as a filter that takes as input live cell coordinates (one cell per line, X and Y coordinates separated by white space), and outputs the live cells of the next generation.
For example, on this input:
-10 100
-10 101
-10 102
1000 5
1001 5
1002 5
the output is:
-11 101
-9 101
1001 4
1001 6
-10 101
1001 5
Here is the code, for bash or similar shells:
> alive.$$
while read cells
do
echo $cells >> alive.$$
set x $cells
x=$2
y=$3
echo $x $((y-1))
echo $x $((y+1))
echo $((x-1)) $((y-1))
echo $((x-1)) $y
echo $((x-1)) $((y+1))
echo $((x+1)) $((y-1))
echo $((x+1)) $y
echo $((x+1)) $((y+1))
done | sort | uniq -c > neighbors.$$
grep '^ *3' < neighbors.$$ | sed -e 's/^ *[0-9].//'
grep '^ *2' < neighbors.$$ | sed -e 's/^ *[0-9].//' > has2.$$
sort alive.$$ -o alive.$$
comm -12 has2.$$ alive.$$
rm has2.$$ neighbors.$$ alive.$$