roacha
roacha

Reputation: 603

Shell Script Help--Accept Input and Run in BackGround?

I have a shell script in which in the first line I ask the user to input how many minutes they want the script to run for:

 #!/usr/bin/ksh
echo "How long do you want the script to run for in minutes?:\c"
read scriptduration
loopcnt=0
interval=1
date2=$(date +%H:%M%S)
(( intervalsec = $interval * 1 ))
totalmin=${1:-$scriptduration}
(( loopmax = ${totalmin} * 60 ))

ofile=/home2/s499929/test.log
echo "$date2 total runtime is $totalmin minutes at 2 sec intervals"
while(( $loopmax > $loopcnt ))
do
  date1=$(date +%H:%M:%S)
   pid=`/usr/local/bin/lsof | grep 16752 | grep LISTEN |awk '{print $2}'` > /dev/null 2>&1
   count=$(netstat -an|grep 16752|grep ESTABLISHED|wc -l| sed "s/ //g")
   process=$(ps -ef | grep $pid | wc -l | sed "s/ //g")
   port=$(netstat -an | grep 16752 | grep LISTEN | wc -l| sed "s/ //g")
  echo "$date1 activeTCPcount:$count activePID:$pid activePIDcount=$process listen=$port" >> ${ofile}
  sleep $intervalsec
  (( loopcnt = loopcnt + 1 ))
done

It works great if I kick it off an input the values manually. But if I want to run this for 3 hours I need to kick off the script to run in the background.

I have tried just running ./scriptname & and I get this:

$ How long do you want the test to run for in minutes:360
ksh: 360:  not found.
[2] + Stopped (SIGTTIN)        ./test.sh &

And the script dies. Is this possible, any suggestions on how I can accept this one input and then run in the background?? Thanks!!!

Upvotes: 6

Views: 7947

Answers (5)

David W.
David W.

Reputation: 107040

Why You're Getting What You're Getting

When you run the script in the background, it can't take any user input. In fact, the program will freeze if it expects user input until its put back in the foreground. However, output has to go somewhere. Thus, the output goes to the screen (even though the program is running in the background. Thus, you see the prompt.

The prompt you see your program displaying is meaningless because you can't input at the prompt. Instead, you type in 360 and your shell is interpreting it as a command you want because you're not putting it in the program, you're putting it in the command prompt.

You want your program to be in the foreground for the input, but run in the background. You can't do both at once.

Solutions To Your Dilemma

You can have two programs. The first takes the input, and the second runs the actual program in the background.

Something like this:

#! /bin/ksh
read time?"How long in seconds do you want to run the job? "
my_actual_job.ksh $time &

In fact, you could even have a mechanism to run the job in the background if the time is over a certain limit, but otherwise run the job in the foreground.

#! /bin/ksh

readonly MAX_FOREGROUND_TIME=30
read time?"How long in seconds do you want to run the job? "
if [ $time -gt $MAX_FOREGROUND_TIME ]
then
    my_actual_job.ksh $time &
else
    my_actual_job.ksh $time
fi

Also remember if your job is in the background, it cannot print to the screen. You can redirect the output elsewhere, but if you don't, it'll print to the screen at inopportune times. For example, you could be in VI editing a file, and suddenly have the output appear smack in the middle of your VI session.

I believe there's an easy way to tell if your job is in the background, but I can't remember it offhand. You could find your current process ID by looking at $$, then looking at the output of jobs -p and see if that process ID is in the list. However, I'm sure someone will come up with an easy way to tell.

It is also possible that a program could throw itself into the background via the bg $$ command.

Some Hints

If you're running Kornshell, you might consider taking advantage of many of Kornshell's special features:

  • print: The print command is more flexible and robust than echo. Take a look at the manpage for Kornshell and see all of its features.

  • read: You notice that you can use the read var?"prompt" form of the read command.

  • readonly: Use readonly to declare constants. That way, you don't accidentally change the value of that variable later. Besides, it's good programming technique.

  • typeset: Take a look at typeset in the ksh manpage. The typeset command can help you declare particular variables as floating point vs. real, and can automatically do things like zero fill, right or left justify, etc.

Some things not specific to Kornshell:

  • The awk and sed commands can also do what grep does, so there's no reason to filter something through grep and then through awk or sed.

  • You can combine greps by using the -e parameter. grep foo | grep bar is the same as grep -e foo -e bar.

Hope this helps.

Upvotes: 1

Tomas
Tomas

Reputation: 59505

So are you using bash or ksh? In bash, you can do this:

{ echo 360 | ./test.sh ; } &

It could work for ksh also.

Upvotes: 0

Usagi
Usagi

Reputation: 2946

You could do something like this:

test.sh arg1 arg2 &

Just refer to arg1 and arg2 as $1 and $2, respectively, in the bash script. ($0 is the name of the script)

So,

test.sh 360 &

will pass 360 as the first argument to the bash or ksh script which can be referred to as $1 in the script.

So the first few lines of your script would now be:

#!/usr/bin/ksh
scriptduration=$1
loopcnt=0
...
...

Upvotes: 3

vstm
vstm

Reputation: 12537

I've tested this with ksh and it worked. The trick is to let the script call itself with the time to wait as parameter:

if [ -z "$1" ]; then
    echo "How long do you want the test to run for in minutes:\c"
    read scriptduration
    echo "running task in background"
    $0 $scriptduration &
    exit 0
else
    scriptduration=$1
fi

loopcnt=0
interval=1
# ... and so on

Upvotes: 0

beny23
beny23

Reputation: 35018

With bash you can start the script in the foreground and after you finished with the user input, interrupt it by hitting Ctrl-Z.

Then type

$ bg %

and the script will continue to run in the background.

Upvotes: 3

Related Questions