Running script in crontab in a specific directory

I am trying to run a script from crontab but the entire script (which is 70+ lines) is written in bash and I need it to run from a certain directory.

So when I run it manually I cd to /local/mnt/scr and then type

./reordersc

and it works fine.

However, I want it to run from the crontab and I will not be able to cd to the /local/mnt/scr directory myself. How do I tell the script to run from that directory?

By adding a cd /local/mnt/scr command in your script ?

Thanks! I will try this out shortly - seems simple but I overlooked it.

Or in crontab + log the output. Example something like:

0 1 * * 1-6 (cd /local/mnt/scr; ./reordersc ) >> /local/mnt/scr/cron.log 2>&1

Actually, it is not failing because of the directory, but because of crontab not "finding" the script. I am getting messages when I run the script in crontab such as:

SHELL = /bin/bash
/local/mnt/scr/reordersc: [[: not found

I checked the script and I've done the following.

  1. Used the #!/bin/bash at the top of the script
  2. used the path variable to set the proper path.
  3. Changed permissions to 755

I tried to run the script from the directory /local/mnt/scr apart from the crontab and it works; using the crontab it fails.

I put the following in the script so it would output to a log file when it ran from the crontab

echo "PATH = $PATH";
echo "SHELL = $SHELL";

It echoed correctly, but still won't run. Any ideas?

This should be without spaces:

SHELL=/bin/bash

Did you export the PATH variable?

I did attempt to export it but I get "not a valid identifer."

[COLOR=\#7a0874]
[b]```text
export PATH=$PATH:/local/mnt:/usr/sbin



is that the wrong syntax?

Try:

PATH=$PATH:/local/mnt:/usr/sbin
export PATH

or in bash:

export PATH=$PATH:/local/mnt:/usr/sbin

Have double checked the typing and it is

export PATH=$PATH:/local/mnt:/usr/sbin

but I still get "not a valid identifier."

Is there a space in you PATH variable? Or did you use export $PATH instead of export PATH . Is there a space elsewhere in the export command?

No, I've got

export PATH=$PATH:/local/mnt:/usr/sbin

I checked for spaces. The only space I can see if between the word export and the word PATH. At the end of the statement, there are also no spaces

Have you done simple script to test ? And if it works, then problem is in the your script code. Cron env PATH is not same as you log in. Compare PATH values. Script need external commands ? PATH need include also those dirs.

Save your env and use those values also in cron env. PATH is the most important. SHELL is not important if your script first line is #!/bin/bash and your bash is in /bin directory.

echo "$PATH
export PATH" >  /local/mnt/scr/myenv

/local/mnt/scr/test1

#!/bin/bash
. ./myenv
date
echo "PATH:$PATH"
echo "0:$0"
chmod a+rx /local/mnt/scr/test1

Add line to the crontab

# run every minute
* * * * * (cd /local/mnt/scr; ./test1 ) >> /local/mnt/scr/cron.test1.log 2>&1

/local/mnt/scr/cron.test1.log include date values ...

I still haven't figured this out, but is there a way to actually run a job as the cron user and see what it is seeing in terms of paths and variables? I can see the crontab is installed under root, but it is not becoming the same user (or it does not have the same environment) as I am when I run the script as root, because my script runs fine from the same location that is being triggered with the crontab.

I have tried everyone's suggestions and they have led me closer to an answer but I have not found one yet.

Log in as the target user, and then run crontab -e and schedule your script. Make sure it explicitly sources your .profile or .bashrc . That should run your script under the that user; c.f. man crontab .
On some systems, there exists /etc/crontab that has an extra user field to be run under.

A good cron script always starts by running .profile, or some such environment file, to get the same behaviors you had interactively. Writing a .profile that is no-terminal friendly is a bit complex. You need to move all the terminal stuff to the end and test something like 'tty' to see if there is a terminal. Similarly, care needs to be taken if profile is to support both sh and ksh or bash, as sh has fewer tools and different options. e.g., no $(...) or 'set -o vi'. You might write a .profile_cron or the like and source it ( . script_path ) from .profile, so the terminal stuff can be left in .profile but all other stuff is in .profile_cron. Ditto for sh versus ksh/bash: .kshrc and .bash_profile

A few things remain different, like 'ps' options that reflect your terminal, for in cron you have no controlling terminal.

A good script, generally, has execute permissions and starts with a valid #! line as described in man execvp. Without that, it usually goes to sh as stdin.

Thanks, everyone of you for all your replies. I finally figured out how to get around this. Apparently there is sudo - su and sudo bash; these are using different profiles, and the crontab uses the sudo - su profile which is stripped down.

I placed bash in front of the path of the script in the crontab and now all my issues are gone!!

Thanks again for all your suggestions, I certainly learned a ton of new tricks!

You do not need the explicit 'bash' in front of the path of the script with a '#!<full_path_of_bash>' line 1 and execute permissions.

You actually do need bash in front of the script. There is something strange about the environment. I found out there were reports of other strange occurrences with any script not written in sh

Well, first off: you shouldn't do that. The correct syntax is:

identifier=content
export identifier

and NOT:

export identifier=content

I know, this is frequently done, which still doesn't make it correct. Notice, btw., that repeatedly exporting variables is not necessary. The oftenly seen:

x=ABC
export x
x=DEF
export x
x=GHI
export x

can be written to the same effect as:

x=ABC
export x
x=DEF
x=GHI

A variable is either exported (inherited by an eventual child process) or not, but once it is it is always exported with the current value.

Another thing is proper quoting. You write:

export PATH=$PATH:/local/mnt:/usr/sbin

and the mentioned combination of "export" and a declaration notwithstanding: what will happen if $PATH" contains white space? To protect your script from breaking in such a case always protect your variables:

PATH="${PATH}:/local/mnt:/usr/sbin"

A last thing: scripts should ALWAYS be written in a way so that they are indifferent to the place where they are started from. This means, first and foremost, you should always use absolute pathes to address files:

WRONG:

inputfile="input.file"
outputfile="output.file"

grep 'something' "$inputfile" | while read line ; do
     do_something "$line"
done > "$outputfile"

This will work or not, depending on which directory the script was started from. Inserting a "cd /some/where" in the first line might help but is bad style (and in the long run dangerous).

CORRECT:

workdir="/some/where"
inputfile="${workdir}/input.file"
outputfile="${workdir}output.file"

grep 'something' "$inputfile" | while read line ; do
     do_something "$line"
done > "$outputfile"

The variables "$inputfile" and "$outputfile" contain now absolute pathes and the script will work regardless from where it is called from.

One very last thing: DGPickett is correct! If your first line looks like:

#! /path/to/your/bash

Then the executable "/path/to/your/bash" is used to run the script, not any other executable at all. Even if there is another (version of) bash installed somewhere (like in "/usr/bin" or whereever) it will not be used, but exactly the specified (executable) file. This is the better solution to use because you specify inside the script by which executable it should be run, not outside of it (where it could be changed without changing the script itself)!

I hope this helps.

bakunin

Hi bakunin,
Actually, the POSIX Standards have required conforming shells to support export commands with the SYNOPSIS forms:

export variable[=word]
      and
export -p

since 1992.

But, of course, if you're working on a project that is still using a pure Bourne shell, you'd still need to avoid assigning a value to a variable in an export command.

1 Like