wc не прокатит grep тоже.. и т.д
Не прокатит потому что нужен был чистый шел...
"Чистого" шелла не существует. Да и expr и cat - это однозначно внешние утилиты. expr позволяет использовать регулярные выражения и гораздо мощнее, чем grep для такой задачи, Ну и, кстати, предложенное решение имеет баг - будет сбоить на файлах, в именах которых есть пробелы.
Можно было бы попробовать, что-то вроде:
#!/bin/sh
DONE=false
until $DONE; do
read s || DONE=true
for w in $s; do
total=$(($total+1))
done
done <"$1"
echo "$total
Но это тоже не только shell - read, true, false, согласно POSIX, не обязаны являться встроенными командами. (Код усложнен из-за заморочек read c последней строкой, в случае, если она не оканчивается символом перевода строки).
А вот это замысловатое решение вроде работает:
cat "$1" | tr '\n' ' ' | tr -s ' ' '\n' | cat -n | tail -1 | (read a b; echo "$a")
Конечно, гораздо проще использовать цикл по `cat "$1"`. Зато этот конвейер легко настроить на то, чтоб он подсчитывал настоящие слова, а не токены, разделенные пробелами.