0

I use the following script to kill process by timeout:

# $1 - name of program and its command line

#launch program and remember PID
eval "$1" &
PID=$!

echo "Program '"$1"' started, PID="$PID

i=1
while [ $i -le 300 ]
do
 ps -p $PID >> /dev/null
 if [ $? -ne 0 ]
  then
   wait $PID
   exit $? #success, return rc of program
  fi

 i=$(($i+1))
 echo "waiting 1 second..."
 sleep 1
done

#program does not want to exit itself, kill it
echo "killing program..."
kill $PID
exit 1 #failed

So far, it have worked excellent, but today, i've noticed a bunch of 'hanging' processes in htop, so i've checked out and it turns out, that $PID in this case is not ID of the program process but of the script itself, and all the times i checked, ID of the program is $PID+1. Now, the question is, am i correct to assume, that it will always be $PID+1 and i won't kill something important by replacing kill $PID with something like kill $PID $($PID+1)

EDIT: $1 may have several urguments, like ./bzip2 -ds sample3.bz2 -k

Shf
  • 3,463
  • 2
  • 26
  • 42
  • No, the PID can be anything. It depends on what runs on the system. – choroba Aug 23 '13 at 11:22
  • You can use `pidof ` which returns the pid of the process. In this case would be `pidof $1`. – Sakthi Kumar Aug 23 '13 at 11:25
  • @SakthiKumar that might be exacly what i need, thank you, i'll test it and reply soon – Shf Aug 23 '13 at 11:27
  • possible duplicate of [Timeout a command in bash without unnecessary delay](http://stackoverflow.com/questions/687948/timeout-a-command-in-bash-without-unnecessary-delay) – dogbane Aug 23 '13 at 11:56
  • @dogbane there is a huge amaunt of files, where this script is used, i am supposed to fix this script, not replace it with a timeout, that will mess a lot of things. And i need return code of a program, not the return code of a timeout. – Shf Aug 23 '13 at 12:09
  • 3
    possible duplicate of [Inside a bash script, how to get PID from a program executed when using the eval command?](http://stackoverflow.com/questions/4339756/inside-a-bash-script-how-to-get-pid-from-a-program-executed-when-using-the-eval). I.e., Try `eval "$1 &"` instead of `eval "$1" &`. – lurker Aug 23 '13 at 12:15
  • @mbratch this might be the case, the behavior exactly like there, though i didn't find that question via search somehow – Shf Aug 23 '13 at 12:23
  • No worries. Give it a try. :) – lurker Aug 23 '13 at 12:24
  • Thanks all for nice replies, but for me `eval "$1 &"` fixed problems, @mbratch i'll accept as answer if you post it, if question will not be closed before this time :) – Shf Aug 23 '13 at 12:37

5 Answers5

3

You can solve the problem simply with the following change:

From:

eval "$1" &

To:

eval "$1 &"

The reason is explained in this answer.

Community
  • 1
  • 1
lurker
  • 56,987
  • 9
  • 69
  • 103
2

I just started writing a script with this functionality. I was going to call it "timeout" but before I opened a blank file, I checked to see if there was already a command with the same name. There was...

timeout

edit

If you need "1" specifically as a return value on failure...

timeout 1 nano -w; `if [[ $? == 124 ]] ; then exit 1 ; fi ; exit $?`
jozxyqk
  • 16,424
  • 12
  • 91
  • 180
  • 1
    i knew about this command, but fom man `If the command times out, then exit with status 124` does not fit. There are a ton of legacy scripts, where "1" is expected as return code from hanging programs, so i'm tied up here – Shf Aug 23 '13 at 12:20
  • @Shf If the command hasn't finished yet, it won't have an output. What do you want instead? – jozxyqk Aug 23 '13 at 12:24
  • timeout will return 124 after timeout kill, yet in a huge bunch of scripts 1 is expected as return in this case – Shf Aug 23 '13 at 12:29
  • Thanks for nice replies, but for me eval "$1 &" fixed problems – Shf Aug 23 '13 at 12:38
1

What's wrong with plain

( eval "$1" ) &
sleep 300
kill %1
choroba
  • 231,213
  • 25
  • 204
  • 289
1

You are backgrounding eval, not the command it runs, and eval is a shell built-in, so you are forking a new shell; that's why (I think) $! is the PID of the current shell.

One simple solution is to avoid using eval (for this and the usual concerns over security).

$1 "$@" &
PID=$!

True, this doesn't allow you to pass an arbitrary bash command line (pipeline, && list, etc) to your script, but your use case may not need to support such generalization. What commands do you typically pass?

chepner
  • 497,756
  • 71
  • 530
  • 681
  • eval is needed, becouse of `<` and `>` are frequent used, like `./cc_dry2 < dhryinput > results` , without `eval` bash is confused – Shf Aug 23 '13 at 12:28
0

Also, here is some refactoring to your code, maybe you will learn something from it:

#launch program and remember PID
eval "$1" &
PID=$!

echo "Program '$1' started, PID=$PID" # you can safely use single quotes inside double quotes, your variables are going to work in  " " as well!

i=1
while (( i <= 300 )) # use (( )) for math operations!
do
    ps -p "$PID" >> /dev/null # it is a good rule to quote every variable, even if you're pretty sure that it doesn't contain spaces
    if [[ $? != 0 ]]; then # Try to use [[ ]] instead of [. It is modern bash syntax
        wait "$PID"
        exit "$?" #success, return rc of program
    fi
    ((i++))
    echo "waiting 1 second..."
    sleep 1
done

#program does not want to exit itself, kill it
echo "killing program..."
kill "$PID"
exit 1 #failed
Aleks-Daniel Jakimenko-A.
  • 10,335
  • 3
  • 41
  • 39