Can run a ".sh" script as the user but not from the crontab

Hello Everyone,

I am logged in as me. I created a script in the directory "/install/new" called "script1.sh" which basically runs another script "runapp.sh" . The "runapp.sh" is a vendor provided application strart up script that came with the installation. This is also in the same directory as "script1.sh". Briefly, my "script1.sh" looks like this:

#!/bin/ksh
m=`ps -ef | grep /install/new/application | wc -l`
if [ $m != 2 ]; then
echo "starting application"
./runapp.sh
else
echo "no action taken";
fi

I have put this script in Cron Job and I edited and saved the Crontab as the same user account that created the "script1.sh".

Here is what my crontab looks like:

$crontab -e

30 * * * * /install/new/script1.sh >> /install/new/log.txt 2>&1

Which is saying run my script every 30 minutes.

Here's the deal- When I test my script "script1.sh" running it from the putty terminal by doing ./script1.sh it runs successfully however when this is run from the Crontab it complains saying this in the "log.txt"

$more log.txt
starting application
/install/new/script1.sh [5]: ./runapp.sh:  not found

What am I doing wrong? Any suggestions will be greatly appreciated.

Thanks in Advance

You are falling for "scripting error number 1": you depend on an environment, which cannot be taken for granted.

Long form: a construct like "./runapp.sh" is telling the shell to execute a program located in the CURRENT directory (the "./" part of the path). This might be the case if you are logged in as your user and manually changed to this directory before. This is NOT the case when cron tries to execute your script, because cron doesn't know that it should manually change into a certain directory first.

Avoid relative paths (everything starting with a "./" in scripts.

Additionally i wonder why the rest of the script worked at all. A command like "ps" is basically an executable and probably located in "/usr/bin/ps". Without a PATH variable pointing to /usr/bin ps shouldn't be found at all. This is also relying on an environment which is probably not set.

In scripts you better set ALWAYS your own environment, because this way you will never encounter problems like these. Here is a sketch of what i mean:

#!/bin/ksh
# Example script

# ----------------- global environment
PATH=/usr/bin:/usr/sbin:<and whatever you need>
export PATH
TERM=vt100
export TERM
# ..... similar for whatever you might also need in your script .....

# -----------------  constants
BINDIR=/path/to/my/scripts
# ..... similar for whatever you might also need in your script .....

# -----------------  script code itself
m=`ps -ef | grep /install/new/application | wc -l`
if [ $m != 2 ] ; then
     echo "starting application"
     $BINDIR/runapp.sh
else
     echo "no action taken"
fi

exit 0

Another (minor) observation: if you use ksh do NOT use "echo" for output. There is the built-in "print" command for that, whereas "echo" is an external command.

I hope this helps.

bakunin

1 Like

Just add the full path in your script. Change ./runapp.sh to

/install/new/runapp.sh

Also, if required make the script executable.
Once you see results, then do to the advanced advice given above.

Another minor observation. This says run your script every hour on the half hour.

A couple of points.

ksh
type echo
echo is a shell builtin

To find out the environment while running ksh from cron, create a one-off script to run once from cron containing the following lines. Output is in root mail or (mail for the owner if this is not a root cron).

#!/bin/ksh
echo "==="
echo "pwd"
echo "==="
pwd
echo "==="
echo "env"
echo "==="
env
echo "==="
echo "set"
echo "==="
set
echo "===="
echo "date"
echo "===="
date
echo "====="
echo "umask"
echo "====="
umask

Now try running the same script from a login prompt and from an "at" job. This should help decide what environment to build into your main script so that it will run from the command line, "at" and "cron".

I would expect "ps" to be available from cron's limited PATH.

So you should change it like */30 instead of just 30 to mean every 30 minutes.

If your system supports it; otherwise 0,30.

Thank you guys for all the suggestions. I put the full path and the script and now works good.

Yes, that is correct.

One thing I have noticed though is that the script is behaving weirdly and that the script is running "runapp.sh" even though it shouldn't.

I added one more line to my script to give me little more details in the log file. The new line is highlighted in bold letters.

#!/bin/ksh
m=`ps -ef | grep /install/new/application | wc -l`
if [ $m != 2 ]; then
echo "`ps -ef | grep /install/new/application`"
echo "starting application"
/install/new/runapp.sh
echo "done"
else
echo "no action taken";
fi

As you see the code says run "runapp.sh" only when the value of $m is not equal to 2. But I found that once in a while the script runs "runapp.sh" even though the value of m = 2. This happens some times and not always. Here are the excerpts from my log file.

no action taken
no action taken
no action taken
no action taken
 bhaire 22082     1  0   Sep 09 ?        4:58 /install/new/application
 bhaire 11402 11394  0 14:45:00 ?        0:00 grep /install/new/application
starting application
done
no action taken
no action taken

Shouldn't the value for "wc -l" be 2. But still why does it go and start "runapp.sh"?

Thanks in advance.

This process is a background process with a PPID of 1 . I suspect the the first "ps" in your script picked up the script or program which started the process "/install/new/application". That isn't in a cron as well is it?

There are other minor issues in the script. You are doing a text compare when a numeric compare would be better. There is a spurious semi-colon near the end of the script. Your highlighed line does not need the echo. Indentation makes scripts easier to follow.

#!/bin/ksh
m=`ps -ef | grep "/install/new/application" | wc -l`
if [ $m -ne 2 ]
then
	ps -ef | grep "/install/new/application"
	echo "starting application"
	/install/new/runapp.sh
	echo "done"
else
	echo "no action taken"
fi

If the user is always "bhaire", "ps -fubhaire" is more efficient than "ps -ef".

Change the code to follows:

 #!/bin/ksh
m=`ps -ef | grep "/install/new/application" | grep -v grep`
if [ $? = 0 ]
then

I made the code cleanup like Methyl suggested and it looks a lot better. I followed Edidataguy's suggestion and added grep -v grep and its working like a charm.

Thank you for all your help and suggestions.