I haven’t quite been able to do it using _only_ builtins, but if you allow the sleep command (which has been standardised since the first version of POSIX, so it should be available pretty much anywhere that makes any sort of attempt to be POSIX compliant), then this seems ok:
# TIMEOUT SYSTEM
#
# Defines a timeout function:
#
# Usage: timeout <num_seconds> <command>
#
# which runs <command> after <num_seconds> have elapsed, if the script
# has not exited by then.
_alarm() {
local timeout=$1
# Spawn a subshell that sleeps for $timeout seconds
# and then sends us SIGALRM
(
sleep "$timeout"
kill -ALRM $$
) &
# If this shell exits before the timeout has fired,
# clean up by killing the subshell
subshell_pid=$!
trap _cleanup EXIT
}
_cleanup() {
if [ -n "$subshell_pid" ]
then
kill "$subshell_pid"
fi
}
timeout() {
local timeout=$1
local command=$2
trap "$command" ALRM
_alarm "$timeout"
}
# MAIN PROGRAM
times_up() {
echo 'TIME OUT!'
subshell_pid=
exit 1
}
timeout 10 times_up
for i in {1..20}
do
sleep 1
echo $i
done