How to execute a shell script using cronjob? [duplicate] - shell

I have my php script file in /var/www/html/dbsync/index.php. When cd /var/www/html/dbsync/ and run php index.php it works perfectly.
I want to call PHP file through sh file, the location of SH file is as below
/var/www/html/dbsync/dbsync.sh
This is the content of the dbsync.sh file is:
/usr/bin/php /var/www/html/dbsync/index.php >> /var/www/html/dbsync/myscript.log 2>&1 -q -f
When I cd /var/www/html/dbsync/ and run ./dbsync.sh it works perfectly as well.
Now if I set up crontab as below:
1 * * * * /var/www/html/dbsync/dbsync.sh /var/www/html/dbsync
However, this crontab is not working as expected.
What can be wrong?

As seen in comments, the problem is that you are not defining what program should be used to execute the script. Take into account that a cronjob is executed in a tiny environment; there, not much can be assumed. This is why we define full paths, etc.
So you need to say something like:
1 * * * * /bin/sh /var/www/html/dbsync/dbsync.sh /var/www/html/dbsync
# ^^^^^^^
/bin/sh being the binary you want to use to execute the script.
Otherwise, you can set execution permissions to the script and add a shell-script header telling it what interpreter to use:
#!/bin/sh
If you do this, adding the path of the binary is not necessary.
From Troubleshooting common issues with cron jobs:
Using relative paths. If your cron job is executing a script of some
kind, you must be sure to use only absolute paths inside that script.
For example, if your script is located at /path/to/script.phpand
you're trying to open a file called file.php in the same directory,
you cannot use a relative path such as fopen(file.php). The file must
be called from its absolute path, like this: fopen(/path/to/file.php).
This is because cron jobs do not necessarily run from the directory in
which the script is located, so all paths must be called specifically.
Also, I understand you want to run this every minute. If so, 1 * * * * won't do. Intead, it will run at every 1st minute past every hour. So if you want to run it every minute, say * * * * *.

It is important to understand "login shell" and "interactive shell" what they means.
login shell: is briefly when you sign in with ssh session and get a terminal window where you can enter shell commands. After login the system executes some files(.bashrc) and sets some environment variables such as the PATH variable for you.
interactive shell :After login on a system, you can startup manually shell terminal(s). The system executes some profile file assigned to your account (.bash_profile, .bash_login,.profile). This files also sets some environment variables and initialize PATH variable for your manually opened shell session.
By OS started shell scripts and cron jobs does not fit in above mentioned way for starting a shell. Therefore no any system scripts(.bashrc) or user profiles are executed. This means our PATH variable is not initialized. Shell commands could not found because PATH variable does not point to right places.
This explains why your script runs successfully if you start it manually but fails when you start it via crontab.
Solution-1:
Use absolute path of every shell command instead of only the command name used in your script file(s).
instead of "awk" use "/usr/bin/awk"
instead of "sed" use "/bin/sed"
Solution-2: Initialize environment variables and especially the PATH variable before executing shell scripts!

method 1, add this header in your dbsync.sh:
#!/bin/bash -l
method 2, add bash -l in your cron file:
1 * * * * bash -l /var/www/html/dbsync/dbsync.sh /var/www/html/dbsync

Related

Running a bash script stored in a variable

I want to send the path to a bash script which sources some environmental variables as an argument to another bash script to run it and use the environmental variables. It works well with no arguments if I hard coded the path to the bash script to run it works and I can retrieve the environmental variables in the main script. the problem happens when I send the path as an argument it does not want to run it.
for example if the path is /path/script.bash and I send the path as an argument I get the error that /path/env_set: No such a file or directory
I run the script by this line
. $1 (this doesn't work)
. /path/script.bash (this works)
if I use
bash -c $1
the bash file runs but it does not set the environmental variables to use it in the main script
I don't know why env_set replaces the script name when I use arguments. Is there any approach to achieve this or any work around to achieve my goal?
It sounds like the problem could be either with your quoting, or with relative paths.
Quoting isn't just about spaces, it's also about pathname expansion (ie. []?* characters).
Do
. "$1"
(not . $1)
And remember, if you're giving a relative path for the environment script (or that script uses some relative paths), you will have a problem. Those paths are relative to the pwd - which is wherever you happen to be when you execute the main script (not where any of the script files themselves happen to be located, for example).
Finally, you can debug this problem by throwing echo at the start, and running the script (if it's safe to do that):
echo . "$1"
exit # Add exit here if you don't want to run w/o the vars.
Now you can see what you're actually trying to source.
In script 1, in your main code, you can call and run script 2,
. ./script 2
The first . stands for current shell, and the second . for current directory.
which will create the environment variables for you, and configure any other settings as well in the same terminal.
Afterwards when script 2 has finished running, script 1 would continue to run, and your environment variables which was created in script 2, will be accessible for script 1 to use in the same session.

Bash script behavig odd when running from cron. but ok when running manual

The following script is running git pull and then write the status to file.
#!/bin/bash
git pull
git log -7 > /var/www/domain.com/v.txt
When I run the file manually, the file v.txt is created and the content inside as expected.
bash /var/www/domain.io/update.sh
When I run it via cron the file is created but the content is empty
* * * * * bash /var/www/domain.io/update.sh
Whats wrong?
If I remember correctly the runtime environment of the interactive shell vs cron are different. Specifically the setting of environment variables like PATH can be different. This can effect how scripts run via cron.
As a test you could try the following. Create a small bash shell script that dumps your environment to a file and run this script from both the command line and cron and look for differences.
Eg. file named: r1.sh
#! /bin/bash
env
From the command line:
$ r1.sh > cmd-line.out
From cron
* * * * * /home/your-user-name/r1.sh > /home/your-user-name/cron.out
NOTE: use full path names for shell script and the output file.
When I did this test on my system, there where a number of difference between the two, for example the PATH variable was much shorter.
If this is the case on your system maybe "git" is not available in the cron env.
Anyway, just an idea to try.
I hope this helps.

Cronjob fails to run a bash script giving an error "aws : command not found"

I have a script to invalidate Amazon CloudFront in a .sh file and it works fine when I run it with bash (bash /../filename.sh). I need to invalidate my distribution every thursday, so I wrote a cron job, but it is giving the error "aws: command not found".
This is my cron job
45 10 * * 2 /usr/bin/bash /var/www/cms/file.sh
What I am missing? Why the cron job is failing when bash could run the script?
This is because your system depends on an environment variable for places where to search for executable files and that variable is not set in the cron sessions. This variable is named $PATH. You can see it's contents in your current session by just typing echo $PATH in your terminal.
When a cron session is started for a job to be executed, this variable is not set.
To solve this issue, there are a few ways:
Method 1:
Use full path names in your shell script and do not depend on the PATH variable for finding executables.
Method 2:
Add the following at the beginning in your script (adjust if needed):
PATH=/sbin:/bin:/usr/sbin:/usr/bin
Method 3:
(This method is generally discouraged)
Add the following line in your crontab (adjust if needed):
PATH=/sbin:/bin:/usr/sbin:/usr/bin
You can do echo $PATH in your terminal and copy the output in the above variable in the crontab.
Thank you Sakis your answer worked perfectly for me.
I was baffled why my scripts would run just fine from the command line but error out when run from cron. The reason was i was trying to use an executable from within bash that was not a recognized command due to lack of reference to a path. Now it's clear.
Did exactly this - no root or other sudo required.
From your own shell, echo $PATH. Copy this line and enter in your own cron (affects all jobs in that cron) or at the top of your script.
PATH=/home/myuser/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
And voila !

Crontab: zip: command not found

I have the following script that runs perfectly when I execute it:
#!/bin/bash
zip logs.zip -r *
However when I run it from crontab, i get
/home/ubuntu/script.sh: line 2: zip: command not found
Why is that? Do I need to set up some sort of path? I have no luck searching for it.
Thanks!
Your login shell will have zip in the PATH, however cron will not run commands in a login shell. You can specify environment variables in your crontab file, you should probably set the PATH to the same as your login shell and possibly add some more variables too. I have:
SHELL=/usr/local/bin/bash
MAILTO="my#email.address"
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
So that cron runs bash, sends emails to a working address and uses a decent PATH
Use the following command to find full path of zip
which zip
then use full path is your script... ie:
/usr/bin/zip logs.zip -r *

Cron job not "seeing" a file

I pass the file path, containing variables to be sourced, as an argument to my Bash script.
The file is created on Windows, in case that makes any difference.
The following check is performed:
CONFIG_FILE=$1
if [[ -f ${CONFIG_FILE} ]]; then
echo "Is a file"
. ${CONFIG_FILE}
else
echo "Not a file"
fi
When I run the script manually, from the command line, the check is fine and the variables get sourced.
However, when I set up a Cron job using
*/1 * * * * /full/path/to/script.sh /full/path/to/configfile
I get "Not a file" printed out.
I attempted every single setup I found online to solve this:
setting up environment variables both in crontab and script itself (PATH & SHELL)
sourcing the profile (both . /etc/profile and . /home/user/.bash_profile) both in crontab (before executing the script) and in the script itself.
trying to run crontab with the -u user parameter, but don't have permissions for this (and it doesn't make sense, as I am already logged in as the user who should setup the crontab)
I am setting up the crontab with the proper user under whom the script should be run. The user has access rights to the location of the files (as can be observed through running the script from the command line).
Looking for further advice on what can be attempted next.
What you're doing here is (I think) making sure that there is a separate argument behind your /path/config-file. Your original problem seems to be that on Unix your config file was stated as /path/config-file\r (note the trailing \r). You are doing it by adding an argument -q\r so that the config file itself is "clean" of the carriage return. You could add blabla\r for that matter instead of -q\r. Your script never interprets that extra argument; but if you put it on the cron line then your config file argument is "protected", because there's stuff following it, that's all.
What you also could do, is make sure that your cron defintion is Unix-styled (\n terminted lines) instead of DOS styled (\r\n terminated lines). There's probably a utility dos2unix on your Unix box to accomplish that.
Or you could remove the crontab on Unix using crontab -r and then re-create the crontab using crontab -e. Just don't upload files that were created on MS-DOS (or derived).
Found another attempt and it worked.
I added the -q flag in the cronjob line.
*/1 * * * * /path/script.sh /path/config-file -q
Source: Cron Job error "Could not open input file"
Can someone please explain to me what does it do?
I am not so literate in bash.

Resources