Bash cannot set environment variable - linux

I have this in my crontab:
* * * * * cd /etc && . ./cron.sh>>cron.log
In my cron.sh (which is executable) I have:
#!/bin/sh
echo "hello world"
export MyVar="abcd"
It runs both with cron and manually, however the environment variable is only set when I run it manually with the command:
. ./cron.sh
Can anyone please help. I know its something to do with source but I cant figure it out.
This does not work either:
* * * * * cd /etc && sh ./cron.sh>>cron.log

. will export variables in the current shell, which is the one spawned by the cron, not yours.
If you want to add an extra variable to your shells, use the ~/.profile et al (specifically the /etc/profile that is shared by all users).

I'm trying to set a variable for all users
You cannot do that via a cron job.
In fact, in general, you can't do it at all. The environment variables of a shell cannot be set from outside the shell. The UNIX / Linux operating system architecture doesn't allow it.
You could could set an environment variable for all users via /etc/profile except ...
the /etc/profile file is only executed when a user logs in, and
the user can override any environment variables set there.

Related

Why would one include ". ./.profile" in a crontab entry in Unix?

I want to know the use of . ./.profile whenever we execute cron jobs. I have seen many scripts having this included. The question is, what is the use and what if I don't add it?
Example:
00 1-22 * * 1-5 . ./.profile ; /global/u1/sie/rox/Scripts/Calls.ksh >/dev/null 2>&1
. somefile is the POSIX-compliant equivalent to the bash builtin source: Running source somefile in bash, or . somefile in any POSIX-compliant shell, executes every command inside that script in that existing shell.
In terms of why this is useful in a crontab: cron runs with a very minimal environment -- it may not even have a PATH set, and is unlikely to have many other facilities. If your scripts depend on environment variables being present, it can be necessary to either specify them in the crontab or to source in (that is, execute in the existing shell) a script which defines them.
That said, I advise against this idiom:
.profile is used by login sessions -- sessions with a user interacting with the shell in real-time -- and folks intending to customize their interactive session's behavior are liable to make modifications without keeping scheduled jobs in mind.
It's not obvious by reading your crontab which environment variables ~/.profile will or won't set, and thus difficult to reason about the state of the environment.
Instead, you should set environment variables at the top of your crontab:
PATH=/bin:/usr/bin:/usr/local/bin
VARNAME=VALUE
# ...etc...
0 1-22 * * 1-5 /global/u1/sie/rox/Scripts/Calls.ksh >/dev/null 2>&1
The profile files are the shell profiles, you can add code to it that will run as soon as the shell start up, ./profile is the profile file for Ksh and Bourne, /.bash_profile is for bash /.login is for Tcsh and Csh.
When a script calls the profile it's because it needs something from it, i.e $path variables or even specific commands that it might not have access to. In this case, since cron doesn't have access to much since it runs in a minimal enviroment that script will pull the .profile because it depends on something that's in there.
More info here
and here

cronjob does not execute a script that works fine standalone

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

Ubuntu cron shebang not working

I have a script with this as the shebang #!/usr/bin/env node.
When cron runs my script, I get this error /usr/bin/env: node: No such file or directory.
When i run the script as my user the script runs fine, just not as cron. I'm guessing it's because node is not on the PATH of the user that runs cron?
How can I get this shebang to work with cron?
$ which node gives me
/home/myuser/.nvm/v0.11.14/bin/node
Cron jobs run in a restricted environment. In an interactive shell, your $PATH is probably set in your $HOME/.bash_profile or $HOME/.bashrc. Cron jobs are executed in an environment that hasn't sourced those files, so your user-specific $PATH settings will not be available.
You can see what that environment looks like by temporarily creating a crontab entry like:
* * * * * printenv > crontab-environment
You can explicitly set $PATH in your crontab, either in the command itself:
* * * * * PATH=$PATH:/home/myuser/.nvm/v0.11.14/bin some_command
or in a separate line in your crontab:
PATH = /usr/bin:/bin:/home/myuser/.nvm/v0.11.14/bin
You can't (directly) use the usual PATH=$PATH:/new/dir syntax to append a directory to your $PATH in an environment setting line, because variable references are not replaced in such a line. They are processed in crontab command lines.
man 5 crontab for details.
Another option is to use an explicit full path in the script itself, changing
#!/usr/bin/env node
to
#!/home/myuser/.nvm/v0.11.14/bin/node
You'll need to customize this for each system where node is installed in a different place. Or you can arrange for node (or nodejs?) to be installed in a consistent place.

Unable to change wallpaper using cronjob on ubuntu

My script -
#!/bin/sh
# export DBUS_SESSION_BUS_ADDRESS environment variable
PID=$(pgrep gnome-session)
export DBUS_SESSION_BUS_ADDRESS=$(grep -z DBUS_SESSION_BUS_ADDRESS /proc/$PID/environ|cut -d= -f2-)
DIR="/home/umang/Downloads/Wallpapers"
PIC=$(ls $DIR/*.jpg | shuf -n1)
/usr/bin/gsettings set org.gnome.desktop.background picture-uri file://$PIC
cronjob file -
* * * * * /path/to/script/Wallpaper_Changer.sh
* * * * * date >> /path/to/logfile/CronTest.log
Wallpaper changes correctly via terminal and the dates are logged via cron.
I am running ubuntu 14.04, GNOME Shell 3.12.1.
Help me change wallpaper on gnome as well as unity.
There are quite a few questions that are similar to this on SO, a simple Google search yielded quite a few. There is this one:
Run cronjob as user to change desktop background in Ubuntu
This one:
https://askubuntu.com/questions/25489/wallpaper-change-crontab-jobs-not-working-after-upgrade-from-8-04-to-10-04
This one:
https://askubuntu.com/questions/403918/setting-cron-to-run-a-shell-script-random-wallpaper-from-a-webpage
And probably more. Hope one of these or others online help you out.
I tried all the solutions but none helped. The problem was, I was still unable to access the environment variables(DBUS_SESSION_BUS_ADDRESS). So one could check if those are available first.
What worked for me is simply running the script in current shell environment. This can be done by running the script with a . as in cronjob file -
* * * * * . /path/to/script/Wallpaper_Changer.sh
More information : Global environment variables in a shell script Understanding Unix shells and environment variables

Executing Azure CLI from shell script as cron job

Inside the shell script sing the full path to the Azure CLI & Node JS.
#!/bin/bash
/opt/nodejs/bin/node /opt/nodejs/bin/azure vm disk list > /tmp/tmpfile
/opt/nodejs/bin/node /opt/nodejs/bin/azure vm disk upload $SRCURL $DESURL <KEY>
When executed manually both the commands are getting executed successfully. But when executed using crontab vm disk list command is working but vm disk upload is not working.
Azure JS script requires another cli.js which is been referenced through relative path var AzureCli = require('../lib/cli'); and the cli.js script requires lots of other scripts which is been referenced through relative path.
Is there any way to provide the same environment profile as the user to cron hence it works or is there any best way to make this work without editing individual JS file and rename the relative path to absolute path?
The issue was because the HOME environment variable was not getting set when executed through cron and that was causing the Azure command to fail.
When the HOME variable is set to the specific user's home directory in the script, it works fine.
You can set your path of required file in PATH environment variable in /etc/profile file
export PATH=$PATH:/path to your files
just redeclare environment variable inside the cron.
example crontab:
NODE_ENV=production
* * * * * /bin/sh /path/to/your/shellscript/nodejs
Check for environment variable SHELL=/bin/sh.
Most times /bin/sh is a symbolic link to /bin/bash, but on Ubuntu /bin/sh is a symbolic link to /bin/dash:
$ ls -la /bin/sh
lrwxrwxrwx 1 root root 4 feb 19 2014 /bin/sh -> dash
So in this case you need to redeclare this environment variable:
export SHELL=/bin/bash
In my case, I export the environment variable in my script and have the following in crontab:
# run azure test every 5 minutes
*/5 * * * * /opt/bin/azure-test >/tmp/azure-test.out 2>&1

Resources