+4  A: 

Kinda hacky, but it works. Doesn't work if you have other foreground processes (please help me fix this!)

sleep TIMEOUT & SPID=${!}; (YOUR COMMAND HERE; kill ${SPID}) & CPID=${!}; fg 1; kill ${CPID}

Actually, I think you can reverse it, meeting your 'bonus' criteria:

(YOUR COMMAND HERE & SPID=${!}; (sleep TIMEOUT; kill ${SPID}) & CPID=${!}; fg 1; kill ${CPID}) < asdf > fdsa
strager
Nice try, but...
system PAUSE
(ls -ltR /cygdrive/c/windows (sleep 1s; kill ${SPID}) fg 1; kill ${CPID}) >fdsa
system PAUSE
bash: fg: no job control
system PAUSE
hmm.... echo ${-} ==> himBH. How do I set -m for the subshells?
system PAUSE
@system PAUSE, set -m, I think.
strager
I have job control (set -m) in the login shell. That's the 'm' in the himBH contents of $-, but it seems to disappear for the subshells. Possibly a Cygwin artifact. *grumble*
system PAUSE
Don't use "fg" in scripts. Read "help wait".
lhunath
@strager, +1 for trying, and thanks. Your approach is very similar to others I've seen since I posted, and to some of the things I tried.
system PAUSE
+10  A: 

I think this is precisely what you are asking for:

http://www.bashcookbook.com/bashinfo/source/bash-4.0/examples/scripts/timeout3

Juliano
That's a keen trick, using $$ for the background part. And nice that it will kill a hung tlrbsf immediately. But ugh, you have to choose a polling interval. And if you set the polling too low, it will eat CPU with constant signaling, making the tlrbsf run even longer!
system PAUSE
You don't have to choose the polling interval, it has a default of 1s, that is pretty good. And the checking is very inexpensive, the overhead is negligible. I doubt that would make tlrbsf run noticeably longer. I tested with sleep 30, and got 0.000ms difference between using and not using it.
Juliano
Right, I see that now. And it meets my exact requirements if you set the poll interval == timeout. Also works in pipelines, works with the whole thing backgrounded, works with multiple instances and other jobs running. Sweet, thanks!
system PAUSE
You are welcome!
Juliano
+3  A: 

In 99% of the cases the answer is NOT to implement any timeout logic. Timeout logic is in nearly any situation a red warning sign that something else is wrong and should be fixed instead.

Is your process hanging or breaking after n seconds sometimes? Then find out why and fix that instead.

As an aside, to do strager's solution right, you need to use wait "$SPID" instead of fg 1, since in scripts you don't have job control (and trying to turn it on is stupid). Moreover, fg 1 relies on the fact that you didn't start any other jobs previously in the script which is a bad assumption to make.

lhunath
With access to 100% of the source (and most of the hardware, such as network switches), I would agree that there are probably better solutions than a timeout. But when a 'tlrbsf' is closed-source, binary only, sometimes you have to work around that limitation.
system PAUSE
@lhunath, "in scripts you don't have job control (and trying to turn it on is stupid)" -- Please clarify here: http://stackoverflow.com/questions/690266/why-cant-i-use-job-control-in-a-bash-script
system PAUSE
@system PAUSE: Reply http://stackoverflow.com/questions/690266/why-cant-i-use-job-control-in-a-bash-script/690297#690297 is correct, I also commented on it.
lhunath
+1  A: 

How would you run this timeout3 if the program has pipes in it?

Like, I am running

echo| openssl s_client abc| ./programx | grep xyz | cut abc | grep cde > /tmp/file

openssl s_client has no timeout... If I try

./timeout3 -t 10 echo| openssl s_client abc| ./programx | grep xyz | cut abc | grep cde > /tmp/file

It will go for more than 10 seconds without timeouting.. Any ideas?

hmes
hmes, Please open a new question for this so that it can be properly answered, voted upon, tagged, and found in search results.
system PAUSE
+3  A: 

See also the http://www.pixelbeat.org/scripts/timeout script the functionality of which has been integrated into newer coreutils

pixelbeat
Neat, simple, uses TERM and not KILL. Nice! I had been exploring a trap/wait solution like this when I originally posed the question.
system PAUSE
+2  A: 

I prefer "timelimit", which has a package at least in debian.

http://devel.ringlet.net/sysutils/timelimit/

It is a bit nicer than the coreutils "timeout" because it prints something when killing the process, and it also sends SIGKILL after some time by default.

maxy