Tuesday, April 6, 2010

Crontab error "/bin/sh: root: command not found"

Today I struggled with making the crontab work on my system. I am using cron jobs for the first time. Although I always wanted to understand how it works, esp as I heard that they are good for periodic backups. But it was quite frustrating for me to make it work, especially if you prefer to google without reading the man pages thoroughly. Let me just explain what I was trying to achieve and how the error got resolved. Now I realize I could have saved a lot of time, had I read the man pages :(
But sometimes we are in a hurry and we are not at all interested in understanding how things work, but in making it work as quickly as possible.
For those who want a quick look at resolution of this error I would say, check your cron syntax:
1. If you are making changes in a local cron file using crontab -e, the job entry should contain 6 fields (not the username)
like this:
* * * * * /home/build_auto/echo.sh

A wrong entry like this:
* * * * * root /home/build_auto/echo.sh

would cause cron to interpret "root" as a command.
The syntax "* * * * * root /home/build_auto/echo.sh" is valid for system crontab file /etc/crontab.
Most of the syntax related examples can be found by reading the man page for crontab files:
man 5 crontab

Creating a simple cron job to run a shell script

I am simply trying to create a cron job and which would execute a shell script for me at regular intervals. So first I read through a simple tutorial from where I learn about the basic syntax and the fields.
Now for my simple cron job, I create a simple shell script which will output some data in another text file. And for simplicity I would like to run it every minute. (so that I can quickly confirm how it works)
So here is my simple shell script which will append a string ("test") to another text file (test.txt)
echo.sh
#!/bin/sh
echo "test" >> /home/build_auto/test.txt

This way everytime the script echo.sh is executed, it will append a string "test" in a new line in test.txt. So when our cron job executes perfectly i.e. every minute, we see "test" in every new line.
Say I save my echo.sh in a location : /home/build_auto/
Now you can add a cron job at two places:
1. In the system cron file /etc/crontab
2. And in a new crontab file using the crontab command.
This file is will be stored in /var/spool/cron with the same name as the username.

Editing the System cron file /etc/crontab

This way is not advisable as you would be directly interfering with the system cron file which is required by cron daemon. Still if you would like to add an entry, open /etc/crontab in an editor and add an entry like this:
* * * * * root /home/build_auto/echo.sh

There are seven fields seperated by spaces. For details on the fields read the man page.
The first field is for minute, second for hour, third for day of month, month, day of week, user account which will be used for execution and command name which is the full path of our shell script.
The *s indicate the job will be executed every minute, every hour and so on. Save the /etc/crontab and your job should execute every minute. There is no need to do any service restart.

Editing the user level crontab file using the crontab command

The other way is to create a new crontab file using the option -e (edit) with crontab, which is mostly meant for non-root users. This file will have the same name as the username and can be found at the location: /var/spool/cron

The crontab syntax is similar to the previous one, except that instead of 7 fields, there are only 6. The username is not required.
Create a new crontab file using the command:
crontab -u root -e
or simply

crontab -e

and add an entry like this:
* * * * * /home/build_auto/echo.sh

Remember, no username here, the crontab command has already taken care of it through the -u option. (or through the current user if -u is omitted) Save the file and now your cron script should be executed every minute. Confirm your entry by listing down the crontab list for user root:

99EP68903:/home/build_auto # crontab -u root -l
# DO NOT EDIT THIS FILE - edit the master and reinstall.
# (/tmp/crontab.XXXXosSNdV installed on Mon Apr 5 22:03:11 2010)
# (Cron version V5.0 -- $Id: crontab.c,v 1.12 2004/01/23 18:56:42 vixie Exp $)
* * * * * /home/build_auto/echo.sh
You can also see the same in the file /var/spool/cron/tabs/root.


Making mistakes

In case, as a noob you create an entry "* * * * * root /home/build_auto/echo.sh" using the crontab -e command, you will get mail error messages like this one:

From root@linux.local Mon Apr 5 22:01:01 2010
Return-Path:
X-Original-To: root
Delivered-To: root@linux.local
Received: by linux.local (Postfix, from userid 0)
id CC5ED320408; Mon, 5 Apr 2010 22:01:01 +0530 (IST)
From: root@linux.local
To: root@linux.local
Subject: Cron root /home/build_auto/echo.sh
X-Cron-Env: <SHELL=/bin/sh>
X-Cron-Env: <HOME=/root>
X-Cron-Env:<PATH=/usr/bin:/bin>
X-Cron-Env: <LOGNAME=root>
X-Cron-Env: <USER=root>
Message-Id: <20100405163101.cc5ed320408@linux.local>
Date: Mon, 5 Apr 2010 22:01:01 +0530 (IST)
Status: R

/bin/sh: root: command not found

This can be misleading, and it can be easily misunderstood as if the cron is unable to locate /bin/sh. But in fact cron is trying to execute a command with the name "root", which does not exist.


This is because cron expects a command in the sixth field.

After a few minutes, upon successful executions of the cronjob the test.txt should look like:

99EP68903:/home/build_auto # cat test.txt
test
test
test
test
test
test
test


And one more thing, ensure that in your shell script the PATH of all files resolves to absolute path, any relative path like ./test.txt would resolve through the home directory of the user that is executing the cron job.


#end of post

9 comments:

  1. Hi,

    Thank you for this post, it solved my problem!

    ;-)

    ReplyDelete
  2. Useful stuff Thanks ! I got the root command not found..

    ReplyDelete
  3. You're a life saver. Thank you! :)

    ReplyDelete
  4. Thank you. Solve my problem :).
    I also learned:
    * * * * * run-parts /etc/cron.test == will exec all the files in the cron.test directory.
    While, * * * * * /etc/cron.test/abc == only exec file "abc"

    ReplyDelete
  5. Very usefull. Thanks

    ReplyDelete
  6. This is very useful. Thanks

    ReplyDelete